Why is error checking so important in programming.
Writing programs for AutoCAD is some what different than writing stand
alone executables. Since our programs run inside AutoCAD they need to
look and feel like a regular AutoCAD command, to do this the programmer
must go to great lengths. Probably the biggest complaint I hear from end
users, is programs that don't reset the osnap modes. This is usually
caused by canceling the program before it has completed it's task.
Blaming the user for this is not the answer! If you are going to write
programs for users other than yourself, get used to the idea of making
them un-breakable. When I first started writing programs in AutoLisp I
learned a valuable lesson from one of my co-workers. I had worked all
weekend on this program, making sure it was perfect. I get to work
Monday, run into my co-workers office, explaining about what a great
program I had made over the weekend. I was dying for them to run it, and
see what a great programmer I was. Well they did run it, and with one
keystroke I saw my 'perfect' program go scrolling down the screen in an
uncontrollable manner and came to rest with a horrible *ERROR*. With
floppy in hand and head hanging down I crawled back to my office. From
that day on I would not release a program that did not have proper error
checking.
So where do we start? Well error checking can be as simple as making
sure a variable has a value before proceeding to the next statement.
(setq a 34)
(if a
- place code here -
); if
Using this type of logic throughout your program will make it much
easier to trap any problems that might occur plus make the program exit
cleaner if something happens, like unexpected user input. The very worst
thing a user can do is hit the escape key when running an AutoLisp
program, that will halt the program at once. Fortunately we have the
*error* function we can use to at least do something after the fact like
reset the users osnap modes. A good error trapping function in your
toolbox is essential. The one I use is this:
; author unknown
(defun *error* (msg)
(if
(not
(member msg '("console break" "Function cancelled" "quit / exit abort")))
(princ (strcat "\nError: " msg))
); if
; reset any vars you change in the program
(princ)
);
This version is placed within the body of your main function like this:
(defun c:main ( / *error*)
(setq osm (getvar 'osmode))
(setvar 'osmode 0)
(defun *error* (msg)
(if
(not
(member msg '("console break" "Function cancelled" "quit / exit abort")))
(princ (strcat "\nError: " msg))
); if
; reset any vars you change in the program
(setvar 'osmode osm)
(princ)
);
); defun main
As you can see if an error occurs within our program the *error*
function will reset the osnap mode back to where was when we started.
With the new Visual Lisp in version AutoCAD 2000 we have some more good
error checking functions at our disposal. My favorite is
'vl-catch-all-apply' it will allow you to trap any errors plus keep your
program running through an escape key press. Let me demonstrate what I
mean. The following function asks the user for input, but what happens
if the user hits the escape key? Without running the code, will the user
see the alert box?
(defun c:cat (/ num)
(setq num (getint "Enter An Integer: "))
(if num
(setq ans (* num 233))
; else
(alert "The variable NUM is nil")
)
(princ)
); defun
Surely not, even though 'num' is nil the program will never get to it.
Enter 'vl-catch-all-apply'. Now let us try the same thing only using our
new tool to catch the escape.
,----[ vl-catch-all-apply ]-
| Passes a list of arguments to a specified function and traps any
| exceptions. Returns the result of the function call, if successful.
| If an error occurs, vl-catch-all-apply returns an error object.
`----
,----[ vl-catch-all-error-p ]-
| Determines whether an argument is an error object returned from
| vl-catch-all-apply. Returns T, if the supplied argument is an error
| object returned from vl-catch-all-apply, nil otherwise.
`----
(defun c:cat (/ num)
(setq num
(vl-catch-all-apply
'getint (list "Enter An Integer: ")
)
)
; now we can test 'num' for T or nil
; if 'num' is NOT nil then set it to [num x 233]
; else alert the user.
(if
(not (vl-catch-all-error-p num)) ; num = nil
(setq ans (* num 233))
; else ; num = T
(alert "The variable NUM is nil")
)
(princ)
); defun
Is that not a thing of beauty? When I first saw this I darn near wet
myself!
User control is what we are talking about here, the more control over
user interaction we have, the better the error checking, the better the
the program. One of the first functions I think of when talking about
controlling user input is the 'initget' function.
,----[ initget ]-
| Establishes keywords for use by the next user-input function call
|
| The functions that honor keywords are getint, getreal, getdist,
| getangle, getorient, getpoint, getcorner, getkword, entsel, nentsel,
| and nentselp. The getstring function is the only user-input function
| that does not honor keywords.
`----
Now I'm not going into great detail here, that would take to much time,
but we need to look at some examples to get the idea across. Try running
these samples and read the Visual Lisp documentation "acad_dev.chm". Run
the following program and enter some arbitrary input, like <enter>.
(defun c:dog (/ num)
; Prevents the user from responding to the
; request by entering only ENTER
(initget 1)
(setq num
(vl-catch-all-apply
'getint (list "Enter An Integer: ")
)
)
); defun 'dog'
Notice how, when you responded to the prompt with <enter> you got the
message "Requires an integer value. Enter An Integer: " that's because
we told 'initget' to warn the user if the input was <enter> (1). What
about zero or negative numbers?
(defun c:dog (/ num)
(initget 7)
(setq num
(vl-catch-all-apply
'getint (list "Enter An Integer: ")
)
)
); defun 'dog'
Let's try some keywords. Now when asked for an integer input a 'd'
instead.
(defun c:dog (/ num ans)
(initget 7 "Dog")
(setq num
(vl-catch-all-apply
'getint (list "Enter An Integer: ")
)
)
(if
(not (vl-catch-all-error-p num))
(if
(= num "Dog")
(alert "BARK BARK BARK......")
; else
(setq ans (* num 233))
); if
(alert "Here we go again with the ESCAPE key.....")
); if
); defun 'dog'
- Joke
Question: What does a hair lip dog sound like
Answer: MARK MARK MARK....
One more........
(defun c:dog (/ num d)
(initget 1 "Dog")
(setq num
(vl-catch-all-apply
'getpoint (list "Select Point: ")
)
)
(if
(not (vl-catch-all-error-p num))
(if
(= num "Dog")
(alert "BARK BARK BARK......")
; else
(setq d (distance '(0.0 0.0 0.0) num))
); if
(alert "Here we go again with the ESCAPE key.....")
); if
); defun 'dog'
I hope I have given you some "food for thought" with this short article
on error checking, as always feed back and/or criticism is welcome.
More on error handling can be found here:
http://code.acadx.com/articles/008.htm