找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1097|回复: 4

[他山之石] Error Handling

[复制链接]

已领礼包: 2220个

财富等级: 金玉满堂

发表于 2013-5-13 14:44:32 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 sachindkini 于 2013-5-13 15:11 编辑

Error Handling

Ever used a program only to discover some time later that your Object Snaps have been mysteriously cleared? Or maybe AutoCAD is suddenly behaving differently? These are tell-tale signs that you have aborted a program that is not equipped with an appropriate error handler.

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.


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. This usually induces the situation in which users find themselves resetting their OSnaps. 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 a LISP expression.

The *error* function takes a single string argument passed to it by AutoCAD when an error is encountered. This string describes the error which has caused the*error* function to evaluate.

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:

[pcode=lisp,true](itoa nil)[/pcode]

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 nil value to the itoa function, which was expecting an integer (indicated by the 'fixnump' message).


Creating an Error Handler

The main purpose of an error handler is to restore a user's AutoCAD environment back to its original settings should something in a program goes wrong. To accomplish this, we can redefine the *error* function.

[pcode=lisp,true](defun c:test (/ *error*)  (defun *error* (msg)
    (princ "error: ")
    (princ msg)
    (princ)
  )
  (getstring "\nPress Esc to force an Error...")
  (princ)
)[/pcode]

When running the above program using the command test at the command line, upon pressing Esc at the prompt, the program will be aborted and the error handler will be evaluated.

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.


Notice that the *error* symbol is localised - this ensures that the default AutoCAD error handler is restored following completion of the program. For more information on localising symbols, see my tutorial on Localising Variables.


A More Professional Error Handler...

At this point you might have noticed that upon pressing Esc at the prompt, above the error handler will print a message similar to:

[pcode=lisp,true]error: Function cancelled[/pcode]

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:

[pcode=lisp,true](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)
)
[/pcode]

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

Sometimes programs will change System Variables during execution to achieve certain results, however, if the user hits Esc at some point when running the program, without the use of an appropriate error handler, these System Variables will not be reset to their original settings.

We can reset any settings changed by the program through the use of a user-defined error handler, as the following example demonstrates:

[pcode=lisp,true](defun c:test (/ *error* osmode)  (defun *error* (msg)
    (if osmode
      (setvar 'osmode osmode)
    )
    (if (not (member msg '("Function cancelled" "quit / exit abort")))
      (princ (strcat "\nError: " msg))
    )
    (princ)
  )
  (setq osmode (getvar 'osmode))
  (setvar 'osmode 0)
  (rtos (getreal "\nPress Esc to exit, press Enter to force an error ..."))
  (setvar 'osmode osmode)
  (princ)
)
[/pcode]

The above code will first store the current setting of the OSMODE System Variable before setting it to 0, and will then restores 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 variable osmode 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

In all the examples featured in this tutorial, I have localised the *error* symbol. This ensures that, following completion of the program, the *error* symbol reverts to the value it held before the program was evaluated (of course this value may be [and usually is] nil).

We can witness this behaviour by considering the following code:

[pcode=lisp,true](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)
)
[/pcode]

Upon calling the program with test at the command line and pressing Esc at the prompt, the following message will be displayed:

[pcode=lisp,true]*error* inside of Test Program.[/pcode]

Now, due to the localisation of the *error* symbol in the program, the *error* symbol will now revert to its previous value: that which is defined outside of the program's function definition.

So, if we now type at the command line:

[pcode=lisp,true](itoa nil)[/pcode]

The previously defined error handler will evaluate, to display the message:

[pcode=lisp,true]*error* outside of Test Program.[/pcode]

To reset to the default error handler, we can clear the value of the *error* symbol:

[pcode=lisp,true](setq *error* nil)[/pcode]

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.


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 the same way as the localisation of the *error* symbol, moreover, in my opinion the localisation is a cleaner and more readable solution.

Consider the following code:

[pcode=lisp,true](defun c:test (/ myerror olderror)
  (defun myerror (msg)
    (setq *error* olderror)
    (princ "\n*error* inside of Test Program.")
    (princ)
  )
  (setq olderror *error*
        *error* myerror
  )
  (getstring "\nPress Esc to force an error...")
  (setq *error* olderror)
  (princ)
)
[/pcode]

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 myerror taking a single string argument: msg. This function will act as our redefined error handler.

Next, we have these lines:

[pcode=lisp,true]    (setq olderror *error*           *error* myerror    )[/pcode]

Here, we are storing the function defintion of *error* in a local variable: olderror, and pointing the *error* symbol to our new definition myerror.

This means that if the program encounters an error and the *error* symbol is evaluated, our locally defined function myerror will be evaluated.

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 myerror function so that, should the program be aborted via the error handler, the previous error handler defined is still restored.

This restoration is accomplished by the line:

[pcode=lisp,true](setq *error* olderror)[/pcode]

Hence setting the *error* symbol to its previously stored value.


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,











评分

参与人数 1D豆 +6 收起 理由
XDSoft + 6 很给力!经验;技术要点;资料分享奖!

查看全部评分

论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

已领礼包: 2688个

财富等级: 家财万贯

发表于 2013-5-13 15:46:31 | 显示全部楼层
很好的关于错误处理帖,可惜是英文,看不太懂
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

发表于 2013-5-13 16:12:47 | 显示全部楼层
Thank you very much, but I cannot read english:lol
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 145个

财富等级: 日进斗金

发表于 2013-5-13 17:05:48 | 显示全部楼层
夏生生 发表于 2013-5-13 16:12
Thank you very much, but I cannot read english

不是有个东西叫谷翻的吗。

点评

楼主是印度人  发表于 2013-5-13 22:26
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2024-9-25 14:29 , Processed in 0.528620 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表