CLisp 16:抛出和捕捉异常,try-catch机制

函数error用于抛出异常,带一个参数,可以是字符串,也可以像format一样拼字符串,也可以是一个异常对象,也可以是指向字符串或异常对

象的标识符,例如
(error "some error")
(setq x "some error")  (error x)
(setq y '(a b c)) (error "error info : ~A" y)

 

接下来用handler-case捕捉异常,类似于try-catch
(defun foo() (error "just wrong"))
(handler-case (foo)
  (error () "catch the error"))
运行后输出字符串catch the error,上面代码翻译成C++就是
try
{
   foo();
}
catch (error)
{
   return "catch the error";
}

 

除了基本的error类型,可以用define-condition定义新的异常类型
(define-condition log-entry-error (error)
  ((text :initarg :text :reader text)))
新类型名称是log-entry-error,它的父类是error,有一个参数text,该参数提供初始化方法和读方法。更多问题参考CLISP面向对象编程,定

义类的方法。
修改上面例子:
(defun foo() (error 'log-entry-error :text "just wrong"))
(handler-case (foo)
  (log-entry-error (e) (text e)))
翻译成C++就是
try
{
    foo();
}
catch (log-entry-error & e)
{
    return e.text();
}

 

如果在catch中同时安插error和log-entry-error类型,会出什么样的结果呢?将谁放前面就会匹配谁
(handler-case (foo)
  (error () "error")
  (log-entry-error () "log-entry-error))
匹配error类型,返回字符串error。

 

可以用更底层的宏handler-bind来实现宏handler-case。handler-bind的使用格式如下
(handler-bind
  ((error-type-1 #'(lambda (x) (...)))
   (error-type-2 #'(lambda (x) (...)))
   (error-type-n #'(lambda (x) (...))))
  (do something 1)
  (do something 2)
  (do something n))
也可以将lamda换成其它函数名,只要该函数接收一个参数。

 

使用示例:
(handler-bind
  ((error #'(lambda (x) (print "some error"))))
  (/ 3 0))
值得注意的是,handler-bind虽然捕捉到了异常,但是并不会自动清除异常,上面例子打印了字符串some error后仍然触发了调试器。


可以用return-from从当前栈中返回,并清除异常,例如下面例子不会触发调试器:
(block xx
  (handler-bind
    ((error #'(lambda (c) (return-from xx "the result"))))
    (/ 3 0)))

 

也可以用go跳到下一处代码,并清除异常,例如下面例子不会触发调试器:
(tagbody
  (handler-bind
    ((error #'(lambda (c)
                (print "some error")
                (go label))))
    (/ 3 0))
  label
  (print "next statement"))

 

用宏handler-bind实现下面的代码
(handler-case (foo)
  (error (var) "error")
  (log-entry-error (var) "log-entry-error))
翻译成
(block xx
  (handler-bind
   ((error #'(lambda (c)
        (let ((var c))
             (return-from xx "error"))))
    (log-entry-error #'(lambda (c)
        (let ((var c))
             (return-from xx "log-entry-error")))))
   (foo)))


如果没有任何捕捉异常的分支
(handler-case (foo))
翻译成
(block xx
  (handler-bind
    ()
    (foo)))

 

自己来实现宏handler.case
(defmacro handler.case (expression &rest handler-list)
  `(block block-name
     (handler-bind
       ,(gen-handler-list 'block-name handler-list)
       ,expression))

(defun gen-handler-list (block-name handler-list)
  (cond ((eq handler-list nil) nil)
        (t cons (gen-one-handler  block-name (car handler-list))
                (gen-handler-list block-name (cdr handler-list)))))

(defun gen-one-handler (block-name handler)
  (let ((type (car  handler))
        (var  (cadr handler))
        (act  (cddr handler)))
    (cond ((eq var nil)
              `(,type #'(lambda (c)
                      (return-from ,block-name ,@act))))
          (t
              `(,type #'(lambda (c)
                      (let ((,(car var) c))
                      (return-from ,block-name ,@act))))) )))

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值