TheSwamp
Code Red => AutoLISP (Vanilla / Visual) => Topic started by: Kerry on September 27, 2010, 01:25:55 AM
-
Code and Attachment Revised 2010.09.28
Code and Attachment Revised 2010.09.30
How do I AutoLoad or demand load lisp files as required. ??
I've seen this question dozens of times in forums,
I needed to make some changes to some really old code today so I thought I'd post it.
The file kdub-Auto-LispLoad.Lsp should be pre-loaded via any means that suits the way you work.
Refer to the comments in the code header.
The following type of code is added to either a .MNL file or any Lisp file that loads automatically.
;; Define Stubs to load routine Loaders
(kdub:Auto-LispLoad
"TestFunFile1"
'(("NoNoNo") ("TestFun3" 3) ("TestFun1") ("c:TestCmd1") ("TestFun2" 2))
)
(kdub:Auto-LispLoad "TestFun22" '(("TestFun22")))
(kdub:Auto-LispLoad "TestFun33" '(("TestFun33" 1)))
;; < etc>
As you can see the DemandLoader handles c:xx commands, functions with and without parameters and allows for multiple functions in one source file.
In the examples above, the following stubs are auto-generated. into memory as follows ;
;;-------------------------------------------------
(Defun Testfun1 (/ Qfn)
(If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
(Progn
(Princ "\nInitializing...\n")
(Setq Testfun1 Nil)
(Load Qfn)
)
(Progn
(Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
(Setq Testfun3 Nil)
)
)
(If Testfun1
(Eval (Testfun1))
(Prompt (Strcat "\nerror : Unable To Evaluate " "Testfun1" "\n"))
)
)
;;
(Defun Testfun2 (Arg0 Arg1 / Qfn)
(If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
(Progn
(Princ "\nInitializing...\n")
(Setq Testfun2 Nil)
(Load Qfn)
)
(Progn
(Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
(Setq Testfun3 Nil)
)
)
(If Testfun2
(Eval (Testfun2 Arg0 Arg1))
(Prompt (Strcat "\nerror : Unable To Evaluate " "Testfun2" "\n"))
)
)
;;
(Defun Testfun3 (Arg0 Arg1 Arg2 / Qfn)
(If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
(Progn
(Princ "\nInitializing...\n")
(Setq Testfun3 Nil)
(Load Qfn)
)
(Progn
(Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
(Setq Testfun3 Nil)
)
)
(If Testfun3
(Eval (Testfun3 Arg0 Arg1 Arg2))
(Prompt (Strcat "\nerror : Unable To Evaluate " "Testfun3" "\n"))
)
)
;;
(Defun C:Testcmd1 (/ Qfn)
(If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
(Progn
(Princ "\nInitializing...\n")
(Setq C:Testcmd1 Nil)
(Load Qfn)
)
(Progn
(Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
(Setq Testfun3 Nil)
)
)
(If C:Testcmd1
(Eval (C:Testcmd1))
(Prompt (Strcat "\nerror : Unable To Evaluate " "C:Testcmd1" "\n"))
)
)
;;
;; and this one should spit the dummy because the function is not defined in the file.
;;-------------------------------------------------
(Defun Nonono (/ Qfn)
(If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
(Progn
(Princ "\nInitializing...\n")
(Setq Nonono Nil)
(Load Qfn)
)
(Progn
(Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
(Setq Testfun3 Nil)
)
)
(If Nonono
(Eval (Nonono))
(Prompt (Strcat "\nerror : Unable To Evaluate " "Nonono" "\n"))
)
)
;;-------------------------------------------------
Any subsequent use of any of these functions/Commands will Auto-load the correct source file and over-write the stub definition with the full definition from the file.
This can be tested by unzipping the sample files into a folder on your search path and progressively running each of these statements.
(vl-load-com)
;;Test .. ensure the Functions are not defined ... just for this test
(setq TestFun1 nil
TestFun2 nil
TestFun3 nil
TestFun22 nil
c:TestCmd1 nil
TestFun33 nil
nonono nil
)
;; Define Stubs to load routine Loaders
(kdub:Auto-LispLoad
"TestFunFile1"
'(("NoNoNo") ("TestFun3" 3) ("TestFun1") ("c:TestCmd1") ("TestFun2" 2))
)
(kdub:Auto-LispLoad "TestFun22" '(("TestFun22")))
(kdub:Auto-LispLoad "TestFun33" '(("TestFun33" 1)))
;; If you highlight and right click on the functionName in the VLIDE then select [b]Inspect...[/b] you'll see that the functioin is defined.
;; Something like this will be returned #<USUBR @0000000035984bd8 TESTFUN2>
;; Run a routine
(TestFun1)
(TestFun2 100 "Apples")
(setq mmage 82)
(TestFun3 "Mickey" "Mouse" mmage)
(c:TestCmd1)
(nonono)
(TestFun22)
(TestFun33 2)
;;-------------------------------------------------
... you'll see that each file is loaded as any function in it is required. Further calls to that function (or any others in the file) are not reloaded because the stub definition has been over-written by the full definition.
kdub-Auto-LispLoad.LSP
(vl-load-com)
;;-------------------------------------------------
;;-------------------------------------------------
(defun kdub:Auto-LispLoad_FindQualifiedFile (f / fn)
;; CodeHimBelonga kdub 2001.07.28
;; Revised 2010.09.29
(if (or (setq fn (findfile f))
(setq fn (findfile (strcat f ".VLX")))
(setq fn (findfile (strcat f ".FAS")))
(setq fn (findfile (strcat f ".LSP")))
)
(vl-string-translate "\\" "/" fn)
)
)
;;-------------------------------------------------
(defun kdub:Auto-LispLoad_NoFile (FileName)
;; CodeHimBelonga kdub 2001.07.28
;; Revised 2010.09.27
(alert
(strcat
"The requested file " FileName " "
(if (not (vl-filename-extension FileName))
"(.lsp/.fas/.vlx) "
)
"\nwas not found in the Profile search path folders.\n"
"\nPlease check the installation of the support files"
"\nand 'Options->Files->Path."
)
)
(princ)
)
;;-------------------------------------------------
;;-------------------------------------------------
(defun kdub:Auto-LispLoad (FileName ParamList /)
;; CodeHimBelonga kdub 2001.07.28
;; based on sample code from VitalLisp by Basis Software circa 1999
;; Revised 2010.09.27
;; Updated [Build:2010.09.29-2] (bugs and redundancies)
;;-------------------------------------------------
;; <fileName> - either
;; fully qualified Path/Name/extension
;; or Path/Name {no ext}
;; or Name {no ext}
;; <ParamList> a list of lists representing :-
;; 1) A cons List of FunctionName string and Argument Count
;; eg: ("TestFun2" 2)
;; 2) A list containing the FunctionName string
;; eg: ("TestFun3" )
;; Example : (kdub:Auto-LispLoad "TestFunFile1"
;; '(("TestFun3" 3) ("TestFun1") ("c:TestCmd1")))
;;-------------------------------------------------
;;
(Setq FileName (strcat "\"" (vl-string-translate "\\" "/" FileName) "\""))
;;
(mapcar
(function
(lambda (_cmd / CommandName a ArgS)
(setq CommandName (Read (car _cmd))
a -1
ArgS '()
)
(if (not (vl-symbol-value CommandName))
(progn
(if (setq argsQty (cadr _cmd))
(setq Args
(reverse
(While (< (Setq a (1+ a)) argsQty)
(Setq Args (cons (read (Strcat "Arg" (Itoa a))) Args))
)
)
)
)
(eval
(list
'defun
CommandName
(append Args '(/ qfn))
(list
'if
(list
'setq
'qfn
(list 'kdub:Auto-LispLoad_FindQualifiedFile (read FileName))
)
(list 'progn
'(princ "\nInitializing...\n")
(list 'setq CommandName 'nil)
(list 'load 'qfn)
)
;; stub else
(list 'progn
(list 'kdub:Auto-LispLoad_NoFile FileName)
(list 'setq CommandName 'nil)
)
)
(list 'if
CommandName
(list 'eval (cons CommandName Args))
;; stub else
(list 'prompt
(list 'strcat
"\nError : Unable to evaluate "
(vl-prin1-to-string CommandName)
"\n"
)
)
)
)
)
)
)
)
)
ParamList
)
(princ)
)
;;-------------------------------------------------
;;-------------------------------------------------
(princ)
[edit:kdub] Attachment Revised 2010.09.28
[edit:kdub] Attachment Revised 2010.09.30 [Build:2010.09.29-2]
-
It's what I want.
Seriously study your routine.
-
The arguments are a nice feature, well done. 8-)
-
Thanks Alan, I like it too.
//----------
Anyone have any problems with the functionality or with the code ??
-
Depending on whats being loaded, the ...FindQualified file might be better off reorganized in the order of LSP/FAS/VLX to reduce the calls to (findfile...). In my experience many sequential calls to (findfile...) can cause noticeable delays, so I would be tempted to completely ignore the file search if a fully qualified filename is passed, and let the (load...) error handling take care of things if its not a valid file. Then again, doesn't (load...) automatically consider the standard AutoCAD search paths?
-
so I would be tempted to completely ignore the file search if a fully qualified filename is passed,
It pretty much does .. the qualified search is first, and it only takes a couple of ticks. I'd rather pass all code through the _FindQualifiedFile than try to determine if the FileName IS actually a qualified path ... and we have the bonus of confirming the file exists, even though in a production environment it SHOULD :)
The process is actually pretty quick.
Regarding the order for the extension search I've opted to have the fast loading files searched for first in the belief that once a code file is stable most people would compile the code so it loads faster and is less likely to be accidently modified.
Thanks for the comments
-
I shall definitely study the code when I get a minute, thanks for sharing Kerry as always.
-
There's a bug in there ..
If the function does not exist in the loaded file the stub doesn't get overwritten when the file is loaded, so we end up with the calls going around in ever-decreasing circles.
I'll have a look at it when I get a chance and update the code.
This won't affect definitions that are set up with due diligence.
-
Code and Attachment in first post Revised 2010.09.28
bug fixed.
If function is not in loaded file the stub code handles the situation
Code was
(eval
(list 'defun
CommandName
(append Args '(/ qfn))
(list 'if
(list 'setq
'qfn
(list 'kdub:Auto-LispLoad_FindQualifiedFile
(read FileName)
)
)
(list 'progn
'(princ "\nInitializing...\n")
(list 'load 'qfn)
)
(list 'kdub:Auto-LispLoad_NoFile FileName)
)
(list 'eval (cons CommandName Args))
)
)
now :
(eval
(list 'defun
CommandName
(append Args '(/ qfn))
(list 'if
(list 'setq
'qfn
(list 'kdub:Auto-LispLoad_FindQualifiedFile
(read FileName)
)
)
(list 'progn
'(princ "\nInitializing...\n")
(list 'setq CommandName 'nil)
(list 'load 'qfn)
)
(list 'kdub:Auto-LispLoad_NoFile FileName)
)
(list 'if
(list 'eval CommandName)
(list 'eval (cons CommandName Args))
(list 'prompt
(list 'strcat
"\nError : Unable to evaluate "
(vl-prin1-to-string CommandName)
"\n"
)
)
)
)
)
-
Is that possible to load merely some functions in a vlx file when I need them, not the whole file? There're some shared vlx files on the internet. Sometimes I need to call for a small part of a special vlx, but don't want to load it all, for there will be some unexpected conflicts with my own routines or subs or variables...
-
Is that possible to load merely some functions in a vlx file when I need them, not the whole file? There're some shared vlx files on the internet. Sometimes I need to call for a small part of a special vlx, but don't want to load it all, for there will be some unexpected conflicts with my own routines or subs or variables...
No, not possible, sorry.
-
**Sigh** :cry:
So much to learn and no time to learn it! If I had the time, I could make use of this.
Thanks for sharing Kerry. I will try to remember this boulder of information.
-
Kerry,
Won't this error?
(nil)
Why not omit it (an 'else' expression) completely to return nil?
Also, why do you use a foreach loop, then a mapcar within this foreach loop? Is this not iterating the same data a number of times equal to the length of the 'paramlist'?
One more thing, couldn't
(if (eval CommandName)
...
)
Be replaced with:
(if CommandName
...
)
-
Kerry,
Won't this error?
(nil)
Yes, remove the (nil) in your build .. I'll update
One more thing, couldn't
(if (eval CommandName)
...
)
Be replaced with:
(if CommandName
...
)
Yes it could.
Also, why do you use a foreach loop, then a mapcar within this foreach loop? Is this not iterating the same data a number of times equal to the length of the 'paramlist'?
yes, that (the complete foreach statement) is redundant
Remove the foreach statement and it's matching parenthesis
I was going to look at parts again because there is some other redundancy ..
and I haven't yet bomb-proofed all branches ...
For instance;
In the Stub, The CommandName variable needs to be set to nil if the file is not found.
The Progn following the (lambda (_cmd / CommandName a ArgS) is not mandatory
These issues are to be (sort of) expected when modifying old code and not testing all branches fully.
Thanks for having a look.
-
This is the current auto-generated stub.
I'll have another play tonight before posting the revised DemandLoad generator
[color=teal];; Stub produced in memory by the DemandLoader to AutoLoad Code file when required.[/color]
[color=teal];;[/color]
[color=navy]([/color][color=blue]Defun[/color] Testfun3 [color=navy]([/color]Arg0 Arg1 Arg2 [color=blue]/[/color] Qfn[color=navy])[/color]
[color=navy]([/color][color=blue]If[/color] [color=navy]([/color][color=blue]Setq[/color] Qfn [color=navy]([/color][color=blue]Kdub:Auto-Lispload_Findqualifiedfile[/color] [color=brown]"Testfunfile1"[/color][color=navy]))[/color]
[color=navy]([/color][color=blue]Progn[/color]
[color=navy]([/color][color=blue]Princ[/color] [color=brown]"\nInitializing...\n"[/color][color=navy])[/color]
[color=navy]([/color][color=blue]Setq[/color] Testfun3 Nil[color=navy])[/color]
[color=navy]([/color][color=blue]Load[/color] Qfn[color=navy])[/color]
[color=navy])[/color]
[color=navy]([/color][color=blue]Progn[/color]
[color=navy]([/color][color=blue]Kdub:Auto-Lispload_Nofile[/color] [color=brown]"\"Testfunfile1\""[/color][color=navy])[/color]
[color=navy]([/color][color=blue]Setq[/color] Testfun3 Nil[color=navy])[/color]
[color=navy])[/color]
[color=navy])[/color]
[color=navy]([/color][color=blue]If[/color] Testfun3
[color=navy]([/color][color=blue]Eval[/color] [color=navy]([/color][color=blue]Testfun3[/color] Arg0 Arg1 Arg2[color=navy]))[/color]
[color=navy]([/color][color=blue]Prompt[/color] [color=navy]([/color][color=blue]Strcat[/color] [color=brown]"\nError : Unable To Evaluate "[/color] [color=brown]"Testfun3"[/color] [color=brown]"\n"[/color][color=navy]))[/color]
[color=navy])[/color]
[color=navy])[/color]
-
These issues are to be (sort of) expected when modifying old code and not testing all branches fully.
Thanks for having a look.
No worries, just giving you the heads up :-)
-
Code and Attachment in first post Revised 2010.09.30 [Build:2010.09.29-2]
Modified as noted in threads
Main stub evaluator is now :
(eval
(list
'defun
CommandName
(append Args '(/ qfn))
(list 'if (list 'setq 'qfn
(list 'kdub:Auto-LispLoad_FindQualifiedFile (read FileName))
)
(list 'progn
'(princ "\nInitializing...\n")
(list 'setq CommandName 'nil)
(list 'load 'qfn)
)
;; stub else
(list 'progn
(list 'kdub:Auto-LispLoad_NoFile FileName)
(list 'setq CommandName 'nil)
)
)
(list 'if
CommandName
(list 'eval (cons CommandName Args))
;; stub else
(list 'prompt
(list 'strcat
"\nError : Unable to evaluate "
(vl-prin1-to-string CommandName)
"\n"
)
)
)
)
)
which generates a stub similar to my previous code post.