What I’m trying to do:
I want the functionality of simple-error in another error. I want this for these reasons:
-
to be able to handle it in a separate clause of
handler-case. -
to avoid specifying the message string over and over again…
-
to have debugger invoked when the error occurs.
I’m sort of puzzled this doesn’t happen naturally by default, never paid enough attention to this issue, but this is apparently how it functions… What I am able to do is to signal an error, which I can later handle with handler-case or handler-bind, but this is bad, because I don’t always remember whether the function throws or not, and when it does throw, but I forget to handle it, the function just returns early, as if nothing happened. But if I resume to using simple-error, then my code starts looking like:
...
(signal "Container ~S has no key ~S~&" :container foo :key bar)
...
(signal "Container ~S has no key ~S~&" :container foo :key baz)
...
And so on, all over the place :/ Of course I can dedicate a variable for saving the message text and maybe have a macro to make it shorter, but this doesn’t help really, because it only hides the actual clutter instead of solving the problem.
What I could do so far:
(define-condition missing-key (condition)
((key :initarg :key
:accessor key-of)
(container :initarg :container
:accessor container-of))
(:documentation
"An error rised when a KEY is not in the CONTAINER"
:report
#'(lambda (condition stream)
(unless *print-escape*
(format stream "~&Container ~S has no key ~S"
(container-of condition)
(key-of condition))))))
(handler-bind
((missing-key
#'(lambda (condition)
(invoke-debugger condition))))
(signal 'missing-key :key 'foo :container 'bar))
What happens, however is that reporting function never gets called… when the error is signalled, instead it prints a generic message Condition MISSING-KEY was signalled.
EDIT:
Thanks to sds answer, this is what I have now:
(define-condition missing-key (error)
((key :initarg :key
:accessor key-of)
(container :initarg :container
:accessor container-of))
(:documentation
"An error rised when a KEY is not in the CONTAINER")
(:report
(lambda (condition stream)
(format stream "Container ~S has no key ~S"
(container-of condition)
(key-of condition)))))
(defmacro signal-missing-key (container key)
`(let ((*break-on-signals*
(cond
((null *break-on-signals*) 'missing-key)
((consp *break-on-signals*)
(list 'or 'missing-key (cdr *break-on-signals*)))
(t (list 'or *break-on-signals* 'missing-key)))))
(signal 'missing-key :key ,container :container ,key)))
I could probably make it more generic by passing more arguments down to signal, but this does what I wanted to do initially, so, unless there is a better way to do the same thing, I’ll probably just use this.
You can use
errororcerrorwith your own condition types, if you want the debugger to be invoked unless the conditions are handled otherwise. This also works for conditions that are not descendants ofsimple-error.If you want this behavior for
signal, too, you can set the variable*break-on-signals*to the according type. For example, you could set it totin order to invoke the debugger for every unhandled condition.