HANDLER-CASE替代品不是宏观

时间:2017-05-15 15:36:48

标签: common-lisp

请考虑以下代码:

(define-condition some-condition (error) nil)

(defmethod print-object ((obj some-condition) stream)
  (format stream "HELLO THERE"))

(defmacro error-report-test-aux (fn-to-cause-error error-type-to-catch fn-to-handle-error expected-message)
  `(let ((result-message
           (handler-case (funcall ,fn-to-cause-error)
             (,error-type-to-catch (e) (funcall ,fn-to-handle-error e)))))
     (assert (string= result-message
                      ,expected-message))
     t))

我可以像这样使用它:

(error-report-test-aux (lambda () (error 'some-condition)) 
                       some-condition 
                       #'princ-to-string 
                       "HELLO THERE")

但我想让error-report-test-aux成为一个函数而不是宏,这样我就可以在变量中传递一种条件。

简单地写defun而不是defmacro并删除反引号和逗号不起作用,因为handler-case是宏而且它不评估error-type-to-catch

我的问题是:是否有类似handler-case的内容会评估它的参数(特别是条件类型参数)?

1 个答案:

答案 0 :(得分:5)

是和否: - )

到您的确切问题

没有标准函数可以执行您想要的操作,因为捕获错误需要建立绑定,而且通常需要绑定常量符号(如let/let*中),因为它更容易优化

可能考虑使用handler-bind创建一个“通用”处理程序,然后拒绝处理“不感兴趣”的条件(正如@jkiiski在评论中所建议的那样),但我不确定是否符合您的确切要求(未经测试!):

(defun error-report-test-aux (fn-to-cause-error error-type-to-catch expected-message)
  (catch 'trap
    (handler-bind ((error
                    (lambda (condition)
                      (when (typep condition error-type-to-catch)
                        (throw 'trap (string= (princ-to-string condition)
                                              expected-message))))))
      (funcall fn-to-cause-error))))

,特定于实施

IF 您的实现通过绑定内部全局变量来实现handler-case / handler-bind,您可以使用progv绑定它自己,然后将error-report-test-aux作为一个函数实现。

这可能不是最好的主意(您的代码将与特定实现结合)。

,有点

您可以使用some-condition命名CLOS类并使用泛型函数而不是宏的事实。