AfraLisp Blog

The AutoLisp/Visual Lisp/VBA Resource Website

AfraLisp Blog

Home Newsletter Utter Rubbish Coding Tips AutoCAD Tips Contact Downloads WAUN

Visual Lisp and Errors

In standard AutoLisp, if your program encounters an error of any sort, it passes to the *error* function only one thing, a description of the error, and your program then ends (refer to "Error Trapping" for more details).
Let's have a quick look at this in action. Load and run this little routine :
 

  (defun error-test1 ()

  (setq int (getint "\nEnter Number : "))

  (setq result (apply 'sqrt (list int)))

  (alert (strcat "Result = " (rtos result)))

  (princ)

);defun      

Enter "4" at the command prompt. This should result in an alert dialog displaying the answer "Result = 2".


Now, run it again and enter "- 4".
Your program will come up with an error :

_$ (error-test1)
; error: function undefined for argument: -4

The reason being of course, is that you cannot determine the square root of a negative number. Now let's add our error trap : 

(defun error-test1 ()

  (setq temperr *error*)
  
  (setq *error* trap)

  (setq int (getint "\nEnter Number : "))

  (setq result (apply 'sqrt (list int)))

  (alert (strcat "Result = " (rtos result)))

  (princ)

);defun

(defun trap (errmsg)
	
	(setq *error* temperr)

  	(alert errmsg)
  
	(alert "There was an error!!")
	
   (princ)
  
);defun      

Load and run the program again. The error will be encountered and control will pass to the error trap which will display two alert dialogs, one displaying the error message, and one displaying a user defined error message :

Your program will now stop. In a properly designed program, the error trap function would reset the system to the state it was in before your program started. (eg. snaps would be reset, system variables would be reset, etc).


Now this fine if you're using standard AutoLisp, but if you are using Visual Lisp function, this is another matter. Many of the Visual Lisp functions are designed to be used in the "programming by exception" style. This means they either return useful values if they succeed, or raise an exception if they fail (instead of returning an error value). If your program uses Visual Lisp functions, you must prepare it to catch exceptions, otherwise the program halts, leaving the user at the command prompt.
The advantage of this though, is that your program can intercept and attempt to process errors instead of allowing control to pass to the *error* function.
For this we would use the "vl-catch-all-apply" function which is designed to invoke any function, return the value from the function, and trap any error that may occur. You could call it an "inline local error function."
The function requires two arguments :

  • a symbol identifying a function or "lambda" expression
  • a list or arguments to be passed to the calling function

Here's how we would use it in our program :

(setq result (vl-catch-all-apply 'sqrt (list int)))

Almost exactly the same as the "apply" function hey!
Now load and run the following, entering "- 4" at the command prompt to force and error :

(defun error-test2 ()

  (setq int (getint "\nEnter Number : "))

  (setq result (vl-catch-all-apply 'sqrt (list int)))

  (alert (strcat "Result = " (rtos result)))

  (princ)

);defun

An error message should appear at the Console prompt :

_$ (error-test2)
; error: bad argument type: numberp: #<%catch-all-apply-error%>

Have a look at the variable "result" in the Watch window :

The variable contains an "error object"!!
If the program runs correctly, "vl-catch-all-apply" stores the return value in "result". If the call is unsuccessful, "vl-catch-all-apply" stores an error object in "result".
Using the "vl-catch-all-error-p" function, we can test for this "error object" : 

(defun error-test2 ()

  (setq int (getint "\nEnter Number : "))

  (setq result (vl-catch-all-apply 'sqrt (list int)))

  (if (vl-catch-all-error-p result)

    (progn

      (setq int (abs int))

      (setq result (vl-catch-all-apply 'sqrt (list int)))

    );progn

  );if
  
  (alert (strcat "Result = " (rtos result)))

  (princ)

);defun
      
Load and run this routine entering "- 4" at the command prompt. This should have caused an error, but because we used the "vl-catch-all-apply" function, the error was trapped and by testing for the error using "vl-catch-all-error-p", we were able to rectify the problem by using the "abs" function.
Let's add our normal error function :
(defun error-test2 ()

  (setq temperr *error*)
  
  (setq *error* trap)
  
  (setq int (getint "\nEnter Number : "))

  (setq result (vl-catch-all-apply 'sqrt (list int)))

  (if (vl-catch-all-error-p result)

    (progn

      (setq int (abs int))

      (setq result (vl-catch-all-apply 'sqrt (list int)))

    );progn

  );if
  
  (alert (strcat "Result = " (rtos result)))

  (princ)

);defun

(defun trap (errmsg)
	
	(setq *error* temperr)

  	(alert errmsg)
  
	(alert "There was an error!!")
	
   (princ)
  
);defun
      

Load and run the program, again entering "- 4" at the command prompt.
No error, everything runs smoothly. Now run the program again, but this time hitting "Esc" at the command prompt. This error is not an ActiveX error, therefore the error is passed to our conventional error trap to be processed.
Hey, we've got two "error" functions to play with now!


Another good example of when to us the "vl-catch-all-apply" function, is when dealing with "Selection Sets" using Visual Lisp.
When you first create a selection set in Visual Lisp, all is well and good. But, if you try to create a selection set that already exists, you will raise an error. Here's a way around that problem :

(defun selset_test1 ()

(vl-load-com)

(setq acadDocument (vla-get-activedocument
     (vlax-get-acad-object)))

(setq ssets (vla-get-selectionsets acadDocument))

(if (vl-catch-all-error-p (vl-catch-all-apply 'vla-item (list ssets "$Set")))

  (setq newSet (vla-add ssets "$Set"))
  
     (progn
       
    	(vla-delete (vla-item ssets "$Set"))
       
  	(setq newSet (vla-add ssets "$Set"))
       
     );progn

);if
  
);defun
      
If the selection sets does not exists, the selection set is created. If it does exist, it is first deleted and then created.
 
The AutoLisp/Visual Lisp/VBA Resource Website

Copyright 1999-Perpetuity by AfraLisp

All rights reserved.
Information in this document is subject to change without notice.
Site created and maintained by Kenny Ramage

The AutoLisp/Visual Lisp/VBA Resource Website