首页 > 代码库 > Error Handling 错误处理
Error Handling 错误处理
This tutorials aims to teach you how to create an error handler for your programs to deal with the clean-up operation when something in the code goes wrong.
by:http://lee-mac.com/errorhandling.html
What can go Wrong?
Errors can arise in many forms: from a null user input, to attempting a division by zero; although, what most users don‘t realise is that thumping the Esc key during program execution is also considered an error and will hence abort the program instantaneously.
引起错误的几种常见形式:
空输入;除以0;ESC…….
This usually induces the situation in which users find themselves manually resetting their osnap settings. This is where a suitable error handler comes into its own.
The *error* Function
The *error* function is a user-definable function which will be evaluated when AutoCAD encounters an error when executing an AutoLISP expression.
*error*是用户自定义的Autolisp语句,当AutoCAD遇到错误时候就会执行
(*error* string)
如果 *error* 不为 nil,则当 AutoLISP 的错误条件出现时,就会执行其中的定义,并由 AutoCAD 传给它一个包含错误信息的字符串作为参数。
用户 *error* 函数可以不带参数调用 command 函数(如 (command))。这样可以取消前面由 command 函数执行的 AutoCAD 命令。
返回值: 除使用 vl-exit-with-value 外,本函数无返回值。
The *error* function takes a single string argument, which is supplied by AutoCAD when an error is encountered during program evaluation. This string describes the error which has caused the *error* function to evaluate.
*error* 函数提供了一个简短的说明语句
The standard error handler used by AutoCAD will simply print this argument to the command line; you can see this behaviour first hand by typing at the command line something like:
标准错误处理函数仅仅是在命令行中提示错误。
(itoa nil)
This will in turn cause the standard error handler to evaluate and print the following message at the command line:
; error: bad argument type: fixnump: nil
Indicating that we have passed a value of nil to the itoa function, which was expecting an integer (indicated by the fixnump message).
Creating an Error Handler
(defun c:test ( / *error* ) (defun *error* ( msg ) (princ "error: ") (princ msg) (princ) ) (getstring "\nPress Esc to force an error...") (princ))
Notice that the above *error* function takes a single argument:
msg which, when the *error* function is evaluated, is passed a string describing the error which has occurred; the above function then proceeds to print this message to the command line. This particular function emulates the standard AutoCAD error handler.
msg 这个变量是当错误出现时候由AutoCAD传入的一个字符串,用来描述发生的错误。
Note that the *error* symbol is declared as a local function (localised) - this ensures that the default AutoCAD error handler is restored following completion of the program.
这个错误处理函数被声明为局部函数,这确保超出这个局部函数的作用范围之外,”the default AutoCAD error handler ”(默认的AutoCAD错误处理函数)恢复使用。
A More Professional Error Handler...
Of course, many users will regularly press Esc to exit a program, so a developer would want to suppress this error message in order to better emulate the behaviour of standard AutoCAD commands.
However, at the same time, we would also want to print any other critical error messages to the command line when an error has occurred outside of the user‘s control.
To achieve this, we can define the *error* function in the following way:
(defun c:test ( / *error* ) (defun *error* ( msg ) (if (not (member msg ‘("Function cancelled" "quit / exit abort"))) (princ (strcat "\nError: " msg)) ) (princ) ) (rtos (getreal "\nPress Esc to exit, press Enter to force an error ...")) (princ))
The above if statement simply says:
"If the error message is neither "Function cancelled" nor "quit / exit abort", then print the message."
Now, upon testing the above code, should the user hit Esc at the prompt, the error handler will not print an error message;
however, if the user hits Enter at the prompt, the rtos function is supplied with a nil argument and the error handler will print the relevant error message.
Resetting the User Environment
(defun c:test ( / *error* osm ) (defun *error* ( msg ) (if osm (setvar ‘osmode osm)) (if (not (member msg ‘("Function cancelled" "quit / exit abort"))) (princ (strcat "\nError: " msg)) ) (princ) ) (setq osm (getvar ‘osmode)) (setvar ‘osmode 0) (rtos (getreal "\nPress Esc to exit, press Enter to force an error ...")) (setvar ‘osmode osm) (princ))
The above code will first assign the current setting of the OSMODE system variable to a local variable (osm) before setting the system variable to 0. The program will then restore the original setting following the prompt.
Should the user hit Esc (or Enter) at the prompt, the locally defined *error* function is evaluated.
The error handler tests the local variable osm for a non-nil value, and, should the original setting of the OSMODE system variable be available, the system variable is reset to this value.
Restoring the Previous Error Handler 恢复原有的(默认的)错误处理函数
(defun *error* ( msg ) (princ "\n*error* outside of test program.") (princ))(defun c:test ( / *error* ) (defun *error* ( msg ) (princ "\n*error* inside of test program.") (princ) ) (getstring "\nPress Esc to force an error...") (princ))
外部(全局)错误处理函数可用一下测试:
(itoa nil)
To reset to the default error handler, we can clear the value of the *error* symbol:
(setq *error* nil)
If AutoCAD encounters an error during program execution and the *error* symbol is nil, the default, in-built error handler will be evaluated to print the error message as mentioned earlier in this tutorial.
如果ACAD 遇到错误而这时*error*变量为未定义,默认的,内置错误处理函数(“the default, in-built error handler ”)将会执行。
An Alternative to Function Localisation (通过将函数局部化的一种变通)
Here I will demonstrate another method to reset the previous error handler following program completion, without localising the *error* function symbol. Some error handling tutorials that I have witnessed utilise this method, but note that it will perform in exactly the same way as the localisation of the *error* symbol, moreover, in my opinion the localisation is a cleaner and more readable solution.
“我”认为这种局部化是一种跟简洁更可读的解决方案
(defun c:test ( / myerr olderr ) (defun myerr ( msg ) (setq *error* olderr) (princ "\n*error* inside of test program.") (princ) ) (setq olderr *error* *error* myerr ) (getstring "\nPress Esc to force an error...") (setq *error* olderr) (princ))
I‘m sure that you would concur that the above alternative method is far less succinct than a simple symbol localisation; however, let us dissect the code to reveal the process that is occurring.
我确信你肯定认为上面的变通方法远不如将一个简单的变量局部化更简练,然而,让我们解析一下这个代码来展示其执行过程。
First, we define a function myerr taking a single string argument: msg. This function will act as our redefined error handler.
Next, we have these lines:
(setq olderr *error* *error* myerr)
Here, we are assigning the current function defintion held by the *error* symbol to a local variable: olderr, and pointing the *error* symbol to our new function definition: myerr.
This means that if the program encounters an error and the *error* symbol is evaluated, our locally defined function myerr will be evaluated.
这意味着:如果在 c:test 范围内碰到错误,*error*会被执行,由于*error*的值为myerr ,即 myerr 会被执行。
Now, since we are not localising the *error* symbol, the previous definition of *error* must be restored ‘manually‘. This needs to occur not only at the end of the program but also within the myerr function so that, should the program be aborted via the error handler, the previous error handler defined is still restored.
由于我们并没有将 *error*变量局部化(*error*变量仍是全局的),之前定义的 *error*(可以是其他自定义程序定义的,如果没有被定义,则由ACAD接管)必须被手动的存储起来。*error*原有的定义不仅在程序的结束而且在 myerr 函数的执行中都未改变,因此当程序通过错误处理函数被退出,之前的错误处理函数仍然被存储着(,可以被还原)。
This restoration is accomplished by the line: (恢复*error* )
(setq *error* olderr)
Hence assigning the previously stored function definition to the *error* symbol.
Further Information
The Visual LISP IDE Help Documentation offers further information regarding error handling in AutoLISP - be sure to read the following topics:
AutoLISP Reference ? AutoLISP Functions ? E Functions ? *error*
AutoLISP Developer‘s Guide ? Using the AutoLISP Language ? AutoLISP Basics ? Error Handling in AutoLISP
For an introduction into using the Visual LISP IDE, see my tutorial: An Introduction to the Visual LISP IDE.