caoyin 发表于 2007-3-30 17:22:00

[原创] 出错处理的点滴经验

               出错处理函数的浅析
                     暨
         Acet-error-init 的“解密”
======================================================================
【文    章】caoyin·LongfinTools
【引用资源】部分 Lisp 的源代码来自 AutoCAD R14 BONUS
            Copyright (C) 1997 by Autodesk, Inc.
======================================================================
■■■■ 0 前言
    自一年前邂逅明经通道以来,开始学习 LISP ,收获真的不少。虽然自己的
水平还十分浅薄,但仍欣慰之至。
    我收集并总结了 LISP 出错处理函数的点滴经验,与大家分享。希望能得到
大家的指点和批评。
■■■■ 1 浅析定义出错处理函数的常犯的错误
    ■■ 1.1 概念及其编程应用的意义
      
            我们知道 *error*函数是用来执行程序出错时进行相关错误处理
      的特殊函数操作。就调用方式而言,大家都很熟悉。所以我们要讨论的
      不是技术问题,而是它对我们编程应用的意义。
            一个完整的程序应该具备一个周全的出错处理。因此,出错处理函
      数我们就会频繁的使用。怎样让我们自定义的出错处理能够应对各种各
      样的出错情况呢?
            通常我们的出错处理函数会分为三部分来定义:
      a. 定义初始化设置,如保存 *error* 的当前值,将自定义的出错函数
      赋名为 *error*,放置 UNDO 的开始标记,保存系统变量信息并设置新
      值等等。★这部分通常被放在程序的开始。
      b. 定义自己的 *error*,在出错时自动激活该函数。执行必要的 UNDO
      恢复,系统变量恢复,初始化设置中的全局变量恢复以及其他的图形操
      作等。
      c. 定义正常退出的恢复设置。★这部分通常被放在程序的结束。
      基本的格式如下:
      ;;------------------------------------------------------------
      ;;例1    这是大多数人的写法
      (defun xxx-begin (varlst / cmd)
          (setq cmd (getvar "cmdecho"))
          (setvar "cmdecho" 0)
          (command "_.undo" "_begin");放置UNDO开始标记
          (setvar "cmdecho" cmd)
          (setq $savvarlst (mapcar '(lambda (x)
                                     (list x (getvar x))
                                    )
                                 varlst
                           )         ;系统变量保存及设置
                $olderr    *error*   ;保存 *error* 的当前值
                *error*    xxx-error ;自定义的出错函数赋名为 *error*
          )
          ;...
      )
      (defun xxx-error (msg)
          (princ msg)                ;打印出错信息
          (xxx-end)                  ;调用自定义函数 xxx-end
          ;...                     ;其他出错处理
      )
      (defun xxx-end (/ cmd)
          (if $savvarlst
            (mapcar '(lambda (x)
                      (apply 'setvar x)
                     )
                  $savvarlst
            )                        ;系统变量恢复到初始设置
          )
          (setq *error*    $olderr   ;*error* 恢复到初始设置
                $savvarlst nil       ;全局变量设置为 nil
                $olderr    nil
          )
          (setq cmd (setvar "cmdecho"))
          (setvar "cmdecho" 0)
          (command "_undo" "_end")   ;放置 UNDO 的结束标志
          (setvar "cmdecho" cmd)
          (princ)
      )
      ;;------------------------------------------------------------
      看上去似乎可以了,但在一些特定的情况下还会出错,它仍然不具备通
      用性。
            我们在下面具体分析。

    ■■ 1.2 通用性及出错可能性分析
            我们在出错处理函数要完成的基本任务,通常包括以下几个要素,
      大家可以检验一下自己写的出错函数能否胜任以下要求。
      a. 系统变量设置
            ◇当程序被用户取消或出现错误而中止能否恢复。
            ◇中途设置的(非在初始时设置的)的系统变量,当程序被用户取消
            或出现错误而中止能否恢复。
            用上面(例1)的函数举例如下:
            ;例2---------------------------------------------------
            (defun c:test (/ os)
                (xxx-begin '("cmdecho" "skpoly" "cecolor"))
                (setvar "cmdecho" 0)
                (setvar "skpoly" 1)
                (setvar "cecolor" "1")
                ;...
                (setq os (getvar "osmode"))
                (setvar "osmode" 3333)
                ;...
                ;;出错点,在此按 ESC
                ;...
                (setvar "osmode" os)
                (xxx-end)
            )
            ;-------------------------------------------------------
            在运行以上程序时用户按下 "ESC" ,相信 "cmdecho" "skpoly"
            "cecolor" 能够恢复原值,但 "osmode" 就不行了。
            --------------------------------------------------------
            问:为什么不在初试时的函数中设置?
            答:假如在复杂程序中可能会套嵌调用初始化函数,然 *error*
                  设置只能有一个。
                  (defun c:test (/ ttt)
                  (xxx-begin '("cmdecho" "skpoly" "cecolor"))
                  ...
                  (defun ttt ()
                      (xxx-begin '("osmode"))
                      ...
                  )
                  ...
                  )
            问:为什么不在 xxx-error 中补充定义?
            答:补充定义的 xxx-error 只能适用当前的程序。我们要求的是
                  能够在任意的程序中调用,这就是我讲的“通用性”。
            ---------------------------------------------------------
            其实我们只要把例2中的
                (setq os (getvar "osmode"))
                (setvar "osmode" 3333)
                ...
                ***出错点
                ...
                (setvar "osmode" os)
            改成:
                (setq $savvarlst
                      (cons (list "osmode" (getvar "osmode"))
                            $savvarlst
                      )
                )
                (setvar "osmode" 3333)
                ;...
                ;;出错点,在此按 ESC
                ;...
            就行了,这我们在后面讨论。
      b. UNDO 设置
            谈论 UNDO 设置之前我们可能有必要来了解一下系统变量 UNDOCTL
      的相关设置。下面是 AUTOCAD 的帮助:
      ---------------------------------------------------------------
      (只读)
      类型      整数
      保存位置尚未保存
      初始值    21
      指示 UNDO 命令的“自动”、“控制”和“编组”选项的状态。 系统将
      使用下列位码值之和将该设置存储为一个位码:
      0    UNDO 关闭
      1    UNDO 打开
      2    只能放弃一条命令
      4    打开“自动”
      8    一个编组处于当前活动状态
      16   将缩放和平移操作编组为单个操作
      ---------------------------------------------------------------
      看完上面的帮助,我们来用(例1)中的函数来作测试:
      ;例3-----------------------------------------------------------
      ;我们先作如下操作:
      (command "_.undo" "_control" "_none")
      ;然后我们来测试
         (xxx-begin '())
      ;结果返回命令行操作错误信息:"无效的选项关键字。"
      ;--------------------------------------------------------------
      为什么呢?其实错误在于 xxx-begin 函数中的
            (command "_.undo" "_begin")
      因为我们执行了
            (command "_.undo" "_control" "_none")
      操作之后 UNDO 命令的关键字发生了变化,如下:
         “输入 UNDO 控制选项 [全部(A)/无(N)/一个(O)/合并(C)] <全部>:”

      所以我们需要根据系统变量 UNDOCTL 的值,而对症下药。然而由于该变量
      为只读属性,SETVAR 对它无效。所以我们必须通过命令行操作来完成相关
      设置。

      c. 程序出错后的操作
            同样以上面的例1的函数为例:
      ;例4-------------------------------------------------------------
         (defun c:test (/ p1 p2 e p3)
         (xxx-begin '("osmode"))
         (initget 1)
         (setq p1 (getpoint "\n指定点1: "))
         (initget 1)
         (setq p2 (getpoint p1 "\n指定点2: "))
         (command "_.line" p1 p2 "")
         (setq e (entlast))
         (redraw e 3)
         (initget 1)
         (setq p3 (getpoint p2 "\n指定点3 (或在此按 ESC): ")) ;;出错点
         (command "_.line" p2 p3 "")
         (redraw e 4)
         (xxx-end)
         )
         ;---------------------------------------------------------------
         正常运行例4的程序第一个对象被高亮显示后被恢复,而中途出错的则无法
         恢复。

         d. 先选后执行的功能
             我们知道当 (= 1 (logand 1 (getvar "pickfirst")))返回 T 的情
         况下我们可以先选择对象,而后执行命令操作。如命令 MOVE、COPY 等。
         但是例1的函数在此则无法实现。为什么呢?
             原来我们先选后,然后执行 xxx-begin 中的
         (command "_.undo" "_begin")
         后先选的对象被取消了。
             ActiveX 的方法不失为一种好方法:
         ;---------------------------------------------------------------
         ;初始时
         (setq $acaddoc (vla-get-activedocument (vlax-get-acad-object)))
         (vla-startundomark $acaddoc)
         ;结束时
         (if $acaddoc
         (progn (vla-endundomark $acaddoc) (setq $acaddoc nil))
         )
         ;---------------------------------------------------------------
         上面的方法优劣参半:
             优点--是既能够实现先选后执行的功能,又避免了系统变量 cmdecho
                   的设置。
             缺点--它在理论上等价于
                   (command "_undo" "_begin") 和
                   (command "_undo" "_end")
                   也存在我们在例 3 中的问题。
         于是我们只能在调用 xxx-begin 开始加上以下代码:
         (if (= 1 (logand 1 (getvar "pickfirst")))
         (setq ss (ssgetfirst))
         )
         而在调用 xxx-begin 结束加上以下代码:
         (if (apply 'or ss)
         (apply 'sssetfirst ss)
         )


■■■■ 2 Acet-error-init Acet-error Acet-error-restore 的“解密”
    ■■ 2.1 本程序根据 AutoCAD R14 BONUS 的源码修改,源程序中有不少 bug
         这里做了修正。
    ■■ 源代码;;; ┏━━━┳━━━━━━━━━━━━━━━━━━━┓
;;; ┃LT: ┃ 出错处理函数                         ┃
;;; ┗━━━┻━━━━━━━━━━━━━━━━━━━┛
;;曹饮整理修改
;;____________________________________________________________________________________________________
;; 〓 (lt:error-init lst) 〓
;;[功能] 出错处理初始化
;;[参数] lst---表(元素1 元素2 元素3)
;;               元素1:包含系统变量及其值的列表
;;               元素2:nil--->不操作
;;                     0----->放置 UNDO 的 BEGIN 和 END 标志
;;                     1或T-->放置 UNDO 的 BEGIN 和 END 标志,但出错时 UNDO 回操作前状态
;;               元素3:出错时执行的特殊操作
(defun lt:error-init (lst / ss varl untag reset bar)
(if (= 1 (logand 1 (getvar "pickfirst")))
    (progn
      (setq ss (ssgetfirst))
      (if (not (apply 'or ss)) (setq ss nil))
    )
)
(mapcar 'set '(varl untag reset bar) lst)
(setq $lt-alive$ (if (not $lt-alive$) 1 (1+ $lt-alive$)))
(if (and (> $lt-alive$ 1)
         (= "*LTErr*" (last $lt-error$))
      )
    (progn
      (setq *error* lt:error $lt-alive$ 1)
      (lt:error-restore)
      (setq $lt-alive$ 1)
    )
)
(if (<= $lt-alive$ 0)
    (progn
      (setq $lt-alive$ 0)
      (lt:error-restore)
      (setq $lt-alive$ 1)
    )
)
(if (= $lt-alive$ 1)
    (progn
      (setq $lt-olderror$ *error*)
      (if (or (not (listp $lt-error$))
            (/= "*LTErr*" (last $lt-error$))
          )
      (setq $lt-error$ (list "*LTErr*"))
      )
      (if (or (= (type (car $lt-error$)) 'list)
            (= "*LTErr*" (car $lt-error$))
          )
      (setq $lt-error$ (cons untag $lt-error$))
      (setq $lt-error$ (cons untag (cdr $lt-error$)))
      )
      (if untag
      (lt:undo-init T)
      (setq $lt-undoctl$ nil)
      )
    )
)
(lt:sysvar-set (car lst))
(if (= $lt-alive$ 1)
    (progn
      (setq *error* lt:error)
      (if reset (setq $lt-error$ (append (reverse (cdr (reverse $lt-error$)))
                                       (list reset (last $lt-error$))
                                 )
                )
      )
    )
)
(if ss (apply 'sssetfirst ss))
)
;;____________________________________________________________________________________________________
;; 〓 (lt:error msg) 〓
;;[功能] 出错处理
;;[参数] msg---出错信息。在通常情况下,*ERROR* 函数会根据不同出错情况,而自动返回出错信息,如:“函数
;;               被取消”。
;;               所以本程序需要用户可以通过定义全局变量 $lt-errormsg$ 而改变默认出错信息:
;;               (1) 为字符串---打印 $lt-errormsg$ 的内容,如:(setq $lt-errormsg$ "程序出错")))
;;               (2) 为表达式---通过执行表达式而获取 msg 的值。如:
;;                              (if (= msg "函数被取消") (setq msg "用户中止"))
;;                              但表达式应以表的方式来传递给程序,即在表达式前面加上一个单引号 “'”。
;;                              (setq $lt-errormsg$ '(if (= msg "函数被取消") (setq msg "用户中止")))
;;               (3) 为 T ------显示默认出错信息
;;               (4) 为 nil-----不显示出错信息
(defun lt:error (msg)
(if (and (listp $lt-error$) (= "*LTErr*" (last $lt-error$)))
    (progn
      (mapcar 'eval (cdr (reverse (cdr $lt-error$))))
      (setq $lt-error$ (list (car $lt-error$) (last $lt-error$)))
    )
)
(cond
    ((= (type $lt-errormsg$) 'list) (eval $lt-errormsg$))
    ((= (type $lt-errormsg$) 'str) (setq msg $lt-errormsg$))
    ((not $lt-errormsg$) (setq msg nil))
)
(if msg (princ msg))
(while (not (= (getvar "cmdnames") "")) (command))
(if (and (or (= (car $lt-error$) 1) (= (car $lt-error$) T))
         $lt-undoctl$
      )
    (progn
      (lt:sysvar-set '("cmdecho" 0))
      (while (not (wcmatch (getvar "cmdnames") "*UNDO*"))
      (command "_.undo")
      )
      (command "_end")
      (command "_.undo" "1")
      (while (not (= (getvar "cmdnames") "")) (command))
    )
)
(setq $lt-alive$ 1)
(lt:sysvar-restore)
(setq $lt-alive$ 0)
(lt:undo-restore)
(if $lt-olderror$
    (setq *error* $lt-olderror$ $lt-olderror$ nil)
)
(princ)
)
;;____________________________________________________________________________________________________
;; 〓 (lt:error-restore) 〓
;;[功能] 出错处理恢复
(defun lt:error-restore ()
(setq $lt-alive$ (1- $lt-alive$)
      $lt-error$ (cons (car $lt-error$)
                         (reverse (cons (last $lt-error$)
                                        (cddr (reverse (cdr $lt-error$)))
                                  )
                         )
                   )
)
(if (>= $lt-alive$ 0)
    (lt:sysvar-restore)
    (setq $lt-varlist$ nil)
)
(if (<= $lt-alive$ 0)
    (progn
      (lt:undo-restore)
      (if $lt-olderror$ (setq *error* $lt-olderror$ $lt-olderror$ nil))
    )
)
(princ)
)
;;____________________________________________________________________________________________________
;; 〓 (lt:sysvar-set lst) 〓
;;[功能] 系统变量设置
;;[参数] lst---表。0 和偶数位置的元素:系统变量
;;                   奇数位置的元素:    要设置的系统变量值,对应该元素之前的系统变量。
;;                   格式:            '(系统变量1 系统变量值1 系统变量2 系统变量值2 ...)
;;[返回] 表。表的长度等于 $lt-varlist$ 值为 nil 之后运行 lt:sysvar-set 函数的次数。
;;         每个表元素具备以下格式:((系统变量1 系统变量值1) (系统变量2 系统变量值2) ...)
(defun lt:sysvar-set (lst / lst3 a n b lst2)
(setq lst3 (car $lt-varlist$) n -2)
(repeat (/ (length lst) 2)
    (setq a    (strcase (nth (setq n (+ n 2)) lst))
          b    (nth (1+ n) lst)
          lst2 (append lst2 (list (list a (getvar a))))
    )
    (if (and $lt-varlist$ (not (assoc a lst3)))
      (setq lst3 (append lst3 (list (list a (getvar a)))))
    )
    (setvar a b)
)
(if $lt-varlist$
    (setq $lt-varlist$ (append (list lst3) (cdr $lt-varlist$) (list lst2)))
    (setq $lt-varlist$ (list lst2))
)
)
;;____________________________________________________________________________________________________
;; 〓 (lt:sysvar-restore) 〓
;;[功能] 系统变量恢复
;;[说明] 必须确保变量 $lt-varlist$ 存在,该变量在运行 lt:sysvar-set 函数时设置;
;;         当变量 $lt-alive$ 小于等于 0 时,依次恢复到 lt:sysvar-set 函数最近设置点
;;         当变量 $lt-alive$ 大于 0或为 nil时,本程序恢复到 lt:sysvar-set 函数第一个设置点,且
;;         设置变量 $lt-alive$ 为 nil。
;;[返回] 变量 $lt-alive$ 的当前值
(defun lt:sysvar-restore (/ lst)
(if (<= $lt-alive$ 0)
    (setq lst (car $lt-varlist$) $lt-varlist$ (list lst))
    (setq lst (last $lt-varlist$))
)
(mapcar '(lambda (x) (apply 'setvar x)) lst)
(setq $lt-varlist$ (reverse (cdr (reverse $lt-varlist$))))
)
;;____________________________________________________________________________________________________
;; 〓 (lt:undo-init varset) 〓
;;[功能] UNDO 初始化设置,及放置开始(begin)标记
;;[参数] varset---为 T   时:将返回值赋于全局变量 $lt-undoctl$
;;                  为 nil 时:忽略值赋
;;[返回] 初始化前系统变量 "undoctl" 的值
(defun lt:undo-init (varset / x y z)
(defun x () (getvar "undoctl"))
(defun y (i) (= (logand i (x)) i))
(lt:sysvar-set '("cmdecho" 0))
(setq z (x))
(if (or (= (x) 0) (= (x) 16)) (command "_.undo" "_all"))
(if (or (not (y 1)) (y 2)) (command "_.undo" "_control" "_all"))
(if (y 4) (command "_.undo" "_auto" "_off"))
(while (y 8) (command "_.undo" "_end"))
(while (not (y 8)) (command "_.undo" "_begin"))
(lt:sysvar-restore)
(if (= varset T) (setq $lt-undoctl$ z) z)
)
;;____________________________________________________________________________________________________
;; 〓 (lt:undo-restore) 〓
;;[功能] UNDO 恢复设置,及放置结束(end)标记放置
;;[说明] 必须确保变量 $lt-undoctl$ 存在,即表示开始(begin)标记已经放置且保存 UNDO 初始化前的
;;         "undoctl" 变量值
(defun lt:undo-restore (/ x)
(if $lt-undoctl$
    (progn
      (defun x (i val) (= (logand i val) i))
      (lt:sysvar-set '("cmdecho" 0))
      (while (= 8 (logand 8 (getvar "undoctl"))) (command "_.undo" "_end"))
      (if (/= $lt-undoctl$ (getvar "undoctl"))
      (progn
          (cond
            ((= 0 $lt-undoctl$) (command "_.undo" "_control" "_none"))
            ((x 2 $lt-undoctl$) (command "_.undo" "_control" "_one"))
          )
          (if (x 4 $lt-undoctl$) (command "_.undo" "_auto" "_on"))
      )
      )
      (if (not (x 2 (getvar "undoctl"))) (lt:sysvar-restore))
      (setq $lt-undoctl$ nil)
    )
)
)

    ■■ 2.2 测试以程序(defun c:test (/ p1 p2 e p3 p4)
(lt:error-init (list '("cmdecho" 0 "cecolor" "1") ;;初始时设置的变量
                     0                            ;;
                     '(if e (redraw e 4))         ;;如果出错,取消亮显
               )
)
(princ "\n测试开始!!")
(initget 1)
(setq p1 (getpoint "\n点1: "))
(initget 1)
(setq p2 (getpoint p1 "\n点2: "))
(lt:sysvar-set '("osmode" 0))
(command "_.line" p1 p2 "")
(setq e (entlast))
(redraw e 3)      ;;亮显
(initget 1)
(setq p3 (getpoint "\n点3(在这里按取消): "))
(initget 1)
(setq p4 (getpoint p3 "\n点4: "))
(command "_.line" p3 p4 "")
(lt:error-restore)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
有关其中的参数说明及全局变量说明明天再发

byghbcx 发表于 2007-3-30 18:19:00

不错,可以

howls 发表于 2007-3-30 19:44:00

谢谢LZ分享经验

caoyin 发表于 2007-3-31 09:31:00

<p>接一楼:</p><p>以上共提供了 7 个函数,以下是参数和全局变量的具体用法:</p><p>1. lt:error-init</p><p>&nbsp;&nbsp; 本函数共 1 个参数,参数为表,但表中包含三个元素</p><p><font color="#000000">&nbsp;&nbsp;&nbsp;元素1:&nbsp;&nbsp;包含系统变量及其值的列表,程序在初始时将系统变量依次设置为对应</font></p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;值。</p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 格式为:&nbsp; '(变量1 变量值1 变量2 变量值2 变量3 变量值3 ... 变量n 变量值n)</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;比如:&nbsp;&nbsp;&nbsp; '("cmdecho" 0 "osmode" 0 "cecolor" "1")</font></p><p><font color="#800080"><font color="#ff0000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 中途设置的变量为了确保能够在出错后恢复,可以使用 lt:sysvar-set 函数,</font></font></p><p><font color="#800080"><font color="#ff0000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 后面我们再详细说明。</font></font></p><p><br/><font color="#000000">&nbsp;&nbsp; 元素2:&nbsp;&nbsp;UNDO 的设置</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nil---&gt;不做任何操作</font><font color="#000000"><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0-----&gt;放置 UNDO 的 BEGIN 和 END 标志</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这样确保程序结束时,用户可通过 U 命令,一次取消该操作<br/>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1或T--&gt;放置 UNDO 的 BEGIN 和 END 标志,但出错时 UNDO 回操作前状态</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样确保程序结束时,用户可通过 U 命令,一次取消该操作</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;而且,出错后,程序自动执行(command "undo" 1)的操作</font></p><p><font color="#000000">&nbsp;&nbsp; 元素3:&nbsp;&nbsp;出错时执行的特殊操作。为任意表达式,但以表的方式传递。</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以<font color="#000000"><font color="#ff1111">表达式之前要加一个 ' 符号</font>。</font></font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们通常会需要在程序出错后,执行其他的任何表达式的操作。</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 比如:redraw 函数亮显某图元。出错后 UNDO 的设置无法正常恢复显示。</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以我们可以设置此元素为: '(if en (redraw en 4))</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于多个操作可用 progn 函数连接。</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如:'(progn (if en (redraw en 4))</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if en2 (entdel en2))</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; 全局变量:</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; <font color="#ff0000">$lt-alive$</font> 记录执行 lt:error-init lt:error lt:error-restore 的执行情况</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; 如 $lt-alive$=1 说明已经执行了一次&nbsp;lt:error-init 函数,并且尚未执行 lt:error-restore </font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; 恢复操作。</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; 这个时候,如果用户再次 执行 lt:error-init ,lt:error-init会自动判别情况,先恢复已经记录</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; 在 $lt-error$ 变量里的相关操作后,将 $lt-alive$ 变量归零。再执行 lt:error-init 设置。</font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; <font color="#f70909">$lt-error$ </font></font></p><p><font color="#000000"><font color="#f70909">&nbsp;&nbsp;&nbsp; </font><font color="#000000">lt:error-init 通过 $lt-error$ 变量将出错的相关恢复要求传递给 lt:error 和lt:error-restore</font></font></p><p><font color="#000000">&nbsp;&nbsp;&nbsp; 并将执行过的内容依次删除。</font></p><p><font color="#000000">;;;------------------------------待续</font></p><p><font color="#000000"></font></p><p><font color="#000000"></font></p><p><font color="#000000"></font></p>

xkcjf_1219 发表于 2007-4-13 23:29:00

很有帮助,谢谢楼主。

dwg001 发表于 2007-4-15 09:27:00

透彻

killer9806 发表于 2007-4-17 22:46:00

<p>值得学习</p><p>谢楼主分享</p>

xshrimp 发表于 2007-4-18 22:06:00

........

16335181 发表于 2008-12-1 09:14:00

<p>挖到前排来学习 </p>

xiaoliang200 发表于 2008-12-1 09:21:00

谢谢版主的无私!
页: [1] 2 3 4 5 6
查看完整版本: [原创] 出错处理的点滴经验