请考虑以下代码:
(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))
Run Code Online (Sandbox Code Playgroud)
我可以像这样使用它:
(error-report-test-aux (lambda () (error 'some-condition))
some-condition
#'princ-to-string
"HELLO THERE")
Run Code Online (Sandbox Code Playgroud)
但我想创建error-report-test-aux
一个函数而不是宏,这样我就可以在变量中传递一种条件.
简单地编写defun
代替defmacro
和删除反引号和逗号不起作用,因为handler-case
它是宏并且它不会评估error-type-to-catch
.
我的问题是:是否有类似的东西handler-case
会评估它的参数(特别是条件类型参数)?
是和否:-)
没有标准函数可以执行您想要的操作,因为捕获错误需要建立绑定,并且通常需要绑定常量符号(例如在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))))
Run Code Online (Sandbox Code Playgroud)
如果您的实现实现handler-case
/handler-bind
通过绑定内部全局变量,您可以使用progv
自己绑定它,从而实现您error-report-test-aux
的功能.
这可能不是最好的想法(您的代码变得与特定实现结合).
您可以使用some-condition
命名CLOS类并使用泛型函数而不是宏的事实.