Danylo Fedorov
Danylo Fedorov

Reputation: 1091

HANDLER-CASE alternative which is not a macro

So consider the following code:

(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))

I can use it like so:

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

But I wanted to make error-report-test-aux a function instead of macro, so that i can pass to it a type of condition within a variable.

To simply write defun instead of defmacro and remove backquote and commas doesn't work because handler-case is macro and it doesn't evaluate error-type-to-catch.

My question is: Is there something like handler-case that would evaluate it's arguments (specifically condition type argument)?

Upvotes: 3

Views: 328

Answers (1)

sds
sds

Reputation: 60004

Yes and no :-)

No to your exact question

There is no standard function that would do what you want because catching errors requires establishing bindings and one usually want to bind constant symbols (like in let/let*) because it is easier to optimize.

You might consider creating a "universal" handler using handler-bind and then declining to handle "uninteresting" conditions (as suggested by @jkiiski in comments), but I am not sure if that fits your exact requirements (untested!):

(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))))

Yes, implementation-specific

IF your implementation implements handler-case/handler-bind by binding an internal global variable, you can use progv to bind it yourself and thus implement your error-report-test-aux as a function.

This is probably not the best idea (your code becomes wedded to a specific implementation).

Yes, kinda

You can use the fact that some-condition names a CLOS class and use generic functions instead of the macro.

Upvotes: 5

Related Questions