Bruce Tobin wrote:
>
> I have a very basic understanding of the condition system, e.g. how to
> use (ignore-errors) and (handler-case). ...
>
> Here's the kind of thing I want to be able to handle. Suppose
> I design a dialog with one button, whose set-value-fn is
>
> (defun button-1-set-value-fn (x y z)
> (error "Oops! Wrong button!"))
>
> Now I want to be able to run this dialog, trapping this and any other
> errors that may be generated by functions called from the dialog's event
> loop. I tried
>
> (ignore-errors (dialog-1))
>
> but this of course did not trap the error. Obviously I don't want to
> have to put error-trapping code at the beginning of every event-handling
> function, but what is my alternative?
I presume you have an outer loop that calls process-pending-events (or
is it single-event that doesn't block?). First, IMHO you should never
call error but always define and signal your own condition types. You
should also, as I show below, provide a nice way to catch system
errors. Let's see if I can produce something you can easily enough
follow. Let me know if it's too muddled. I'm just following our basic
flow but writing extemporaneous code; so, I won't guarantee that it
runs, but it can't be far wrong.
(define-condition my-applications-conditions ()
(;any slots you want all your applications conditions to have such as
(default-restart
:reader default-restart
:initarg :default-restart
:documentation "if present, is either a symbol naming the restart,
a list whose car is the restart name and cdr is the
list of args, or a restart object. Of course, the code below is too
lazy to handle anything but a symbol or restart object."))
(:default-initargs :default-restart nil)
;;what to say to the user
(:report (lambda (condition stream)
(format stream "This wonderous application signalled an
abnormal <[ at condition~> and wants to jump to ~A~]."
(find-restart
(if (consp (default-restart condition))
(car (default-restart condition))
(default-restart condition))
condition)))))
(define-condition bad-button (my-applications-conditions)
(;;whatever slots make sense such as
(help-context
:type integer
:initarg :help-context
:reader help-context
:documentation "The help context to pass to WinHelp to get relevant
information for when this button applies.")
;;or
(simple-explanation ;...
...))
(:report (lambda (condition stream)
(format stream "..." ...))))
;;even a specific condition for each button
(define-condition bad-button-quux (bad-button) ...)
;;in your button action fn, you'd then signal the right type of
condition
(defun button-1-set-value-fn (x y z)
(restart-case
(signal 'bad-button-quux
:simple-explanation "You may only ask for dessert after you eat
your vegetables"
:default-restart 's-all-right)
(s-all-right ()
:test (lambda (condition) (typep condition 'bad-button-quux))
:report "Oh, yeah, just ignore the button press, thanks.")
(just-eat-the-vegies-for-me ()
:test (lambda (condition) (typep condition 'bad-button-quux))
:report (lambda (stream)
(format stream "Go ahead and eat ~A with ~A for me."
x z))
(eat-vegies x z))))
;;in your top loop
(restart-case
(loop
do (handler-bind
((my-applications-conditions #'my-applications-handler)
(t #'my-application-system-error-handler))
(process-pending-events)
;; anything else in my main loop
))
(exit-and-farewell ()
:test true
:report "Exit this wonderous application"))
;;given the above restart, you can define the exit menu choice as
... (invoke-restart 'exit-and-farewell))
;;you could even define my-applications-conditions as a method
;;and specialize it for different types of conditions
(defun my-applications-conditions (condition)
;;important, wrap the body w/ a handler-bind b/c you're
;;outside your top loop handler. You don't need to wrap
;;any other fn bodies with handler-bind except for
;;my-application-system-error-handler
(handler-bind
((my-applications-conditions #'my-applications-handler)
(t #'my-application-system-error-handler))
(let* ((restarts (restarts-with-default-first condition))
;;actually, you should ensure that your top window
;;is shown (not minimized). Define what happens when the
user
;;hits <escape>.
(choice (a-single-item-list-dialog (or *my-top-window*
*screen*)
"August Wizarg"
(format nil "~A" condition)
(mapcar #'princ-to-string restarts))))
(invoke-restart (nth choice restarts)))))
(defun restarts-with-default-first (condition)
(let ((restarts (compute-restarts condition)))
(if (default-restart condition)
(cons (find (default-restart condition) restarts :key
#'restart-name)
(delete (default-restart condition) restarts :key
#'restart-name))
restarts)))
;;my-application-system-error-handler would be similar to
my-applications-conditions
;;except should know that some errors are irrecoverable such as out of
stack/
;;out of memory and you can do more harm by trying to open a dialog in
those cases.
;;Of course, Franz doesn't have much of a condition hierarchy nor even
well documented
;;condition strings; so, you kind of have to do partial string matching
on the basis
;;of lessons learned or guesses. Another aspect of this is for errors
like out of
;;disk space, you may want to provide additional advice beyond the error
message.
--
Donald H. Mitchell <pgh.net at dhm>
Proactive Solutions, Inc. 412.835.2410
5858 Horseshoe Dr. 412.835.2411 (fax)
Bethel Park, PA 15102