I am writing a wrapper function to C code (still cairo ;-), where
error status needs to be queried explicitly. The following
self-contained code demonstrates what I am trying to do:
(defun query-status () 'success)
(defmacro catch-error (&body body)
(let ((result (gensym))
(status (gensym)))
`(let* ((,result (progn ,@body))
(,status (query-status)))
(if (eq ,status 'success)
,result
(warn "status is ~a" ,status)))))
Now the fly in the ointment: it works fine if body returns a single value:
(catch-error
(+ 1 1))
but not so with multiple values:
(catch-error
(floor pi))
Is there a solution to this that would work in both cases?
Thanks,
Tamas
--
Posted via a free Usenet account from http://www.teranews.com
(defmacro catch-error (&body body)
(let ((result (gensym))
(status (gensym)))
`(let* ((,result (multiple-value-list (progn ,@body)))
(,status (query-status)))
(if (eq ,status 'success)
(apply #'values ,result)
(warn "status is ~a" ,status)))))
---
Geoff
(defmacro catch-error (&body body)
(let ((result (gensym))
(status (gensym)))
`(let* ((,result (multiple-value-list (progn ,@body)))
(,status (query-status)))
(if (eq ,status 'success)
(apply #'values ,result)
(warn "status is ~a" ,status)))))
---
Geoff
The easiest way to fix this is to capture the multiple values out of
the BODY as a list using MULTIPLE-VALUE-LIST. The list is then be
converted back to multiple values using VALUES-LIST.
if you know the number of values returned you can use MULTIPLE-VALUE-
BIND, otherwise you can use MULTIPLE-VALUES-LIST
(defmacro catch-error (&body body)
(let ((result (gensym))
(status (gensym)))
`(let* ((,result (multiple-value-list (progn ,@body)))
(,status (query-status)))
(if (eq ,status 'success)
(apply #'values ,result)
> Hi,
>
> I am writing a wrapper function to C code (still cairo ;-), where
> error status needs to be queried explicitly. The following
> self-contained code demonstrates what I am trying to do:
>
> (defun query-status () 'success)
>
> (defmacro catch-error (&body body)
> (let ((result (gensym))
> (status (gensym)))
> `(let* ((,result (progn ,@body))
> (,status (query-status)))
> (if (eq ,status 'success)
> ,result
> (warn "status is ~a" ,status)))))
>
> Now the fly in the ointment: it works fine if body returns a single value:
>
> (catch-error
> (+ 1 1))
>
> but not so with multiple values:
>
> (catch-error
> (floor pi))
>
> Is there a solution to this that would work in both cases?
(defmacro catch-error (&body body)
(let ((vstatus (gensym)))
`(multiple-value-prog1 (progn ,@body)
(let ((,vstatus (query-status)))
(unless (eq ,vstatus 'success)
(warn "status is ~A" ,vstatus))))))
C/USER[21]> (let ((s t)) (defun query-status () (or (setf s (not s)) 'success)))
QUERY-STATUS
C/USER[22]> (catch-error (values 1 2 3))
1 ;
2 ;
3
C/USER[23]> (catch-error (values 1 2 3))
WARNING: status is T
1 ;
2 ;
3
--
__Pascal Bourguignon__ http://www.informatimago.com/
NOTE: The most fundamental particles in this product are held
together by a "gluing" force about which little is currently known
and whose adhesive power can therefore not be permanently
guaranteed.
> (defmacro catch-error (&body body)
> (let ((vstatus (gensym)))
> `(multiple-value-prog1 (progn ,@body)
> (let ((,vstatus (query-status)))
> (unless (eq ,vstatus 'success)
> (warn "status is ~A" ,vstatus))))))
This is very neat. Thanks for all the replies.
Tamas
> On May 15, 11:43 am, Tamas Papp <tkp...@gmail.com> wrote:
> > Hi,
> >
> > I am writing a wrapper function to C code (still cairo ;-), where
> > error status needs to be queried explicitly. The following
> > self-contained code demonstrates what I am trying to do:
> >
> > (defun query-status () 'success)
> >
> > (defmacro catch-error (&body body)
> > (let ((result (gensym))
> > (status (gensym)))
> > `(let* ((,result (progn ,@body))
> > (,status (query-status)))
> > (if (eq ,status 'success)
> > ,result
> > (warn "status is ~a" ,status)))))
...
> if you know the number of values returned you can use MULTIPLE-VALUE-
> BIND, otherwise you can use MULTIPLE-VALUES-LIST
>
> (defmacro catch-error (&body body)
> (let ((result (gensym))
> (status (gensym)))
> `(let* ((,result (multiple-value-list (progn ,@body)))
> (,status (query-status)))
> (if (eq ,status 'success)
> (apply #'values ,result)
> (warn "status is ~a" ,status)))))
First, do you really just want the result of the call to WARN (that
is, NIL) to be re returned in the warning case? It's usually clearer
to do that explicitly rather than just assume that an operator will
return the right thing.
Second, I'm not sure I see where the "catching" is going on. I think you
should not call this macro catch-xxx unless it catches something. If
instead what it is just passively review error status of normally
returning things and not catch other things, then maybe another name?
NOTICING-UNSUCCESSFUL-RESULT or WARNING-UPON-SUSPICIOUS-RETURN or some
such thing would work better for me. Don't use a name with a meaning
already associated with it unless you mean to attach that meaning.
Third, just for grins, I want to point out that there are sometimes
alternatives to multiple-value-list. I've incorporated one of them here...
(defmacro warning-upon-suspicious-return (&body body)
(let ((bypass (gensym)))
`(block ,bypass
(multiple-value-prog1 (progn ,@body)
(unless (eq (query-status) 'success)
(warn "status is ~A" ,status)
(return-from ,bypass nil))))))
Or else perhaps you want to return the warning...
(defmacro warning-upon-suspicious-return (&body body)
(let ((bypass (gensym "BYPASS")) (status (gensym "STATUS")))
`(block ,bypass
(multiple-value-prog1 (progn ,@body)
(let ((,status (query-status)))
(unless (eq ,status 'success)
(return-from ,bypass (warn-about-suspicious-return ,status))))))))
(defun warn-about-suspicious-return (status)
(let ((warning (make-instance 'simple-warning
:format-control "status is ~A"
:format-arguments (list status))))
(warn warning)
warning))
I didn't test this code, so there might be errors, but hopefully you get
the point.
> (defmacro catch-error (&body body)
> (let ((vstatus (gensym)))
> `(multiple-value-prog1 (progn ,@body)
> (let ((,vstatus (query-status)))
> (unless (eq ,vstatus 'success)
> (warn "status is ~A" ,vstatus))))))
Wow--deja vu.. I had to look twice to make sure I wasn't re-reading
the message I just sent. I guess I should have waited before
replying! (But I'm always afraid I won't be able to find the messages
again that I want to respond to, so I usually just jot something down
while I'm there.)
My code is catching multiple worthless trolls :-)
I decided to test Kent Pitman's code since he said it hadn't been
tested. I used Pascal Bourguignon's procedure QUERY-STATUS:
The first procedure was fine with a minor edit to include a second LET
to bind the variable STATUS to the value returned by QUERY-STATUS. The
second procedure, however, consistently gave the error:
*** - EVAL: variable TROL has no value
Eventually, after replacing WARN-ABOUT-SUSPICIOUS-RETURN and WARNING
with shorter names, TEST-WARNING and WRN, the code worked. On
examining the dribble file I noted that the arguments FORMAT-CONTROL
and FORMAT-ARGUMENTS were being truncated to TROL AND TS
respectively.
I'm not aware of a problem with reasonable name lengths. Here is the
edited dribble file evaluated using CLisp in Emacs Inferior Lisp Mode.
Can you tell what's going on?
[9]>
(let ((s t))
(defun query-status ()
(or (setf s (not s))
'success)))
QUERY-STATUS
[10]>
(defmacro warning-upon-suspicious-return (&body body)
(let ((bypass (gensym))
(status (gensym)))
`(block ,bypass
(let ((,status (query-status)))
(multiple-value-prog1 (progn ,@body)
(unless (eq ,status 'success)
(warn "status is ~A" ,status)
(return-from ,bypass nil)))))))
WARNING-UPON-SUSPICIOUS-RETURN
[11]>
(warning-upon-suspicious-return (values 1 2 3))
1 ;
2 ;
3
[12]>
(warning-upon-suspicious-return (values 1 2 3))
WARNING:
status is T
NIL
[13]>
(defmacro warning-upon-suspicious-return (&body body)
(let ((bypass (gensym "BYPASS"))
(status (gensym "STATUS")))
`(block ,bypass
(multiple-value-prog1 (progn ,@body)
(let ((,status (query-status)))
(unless (eq ,status 'success)
(return-from ,bypass (warn-about-suspicious-
return ,status))))))))
WARNING-UPON-SUSPICIOUS-RETURN
[14]>
(defun warn-about-suspicious-return (status)
(let ((warning (make-instance 'simple-warning
trol "status is ~A"
ts (list status))))
(warn warning)
warning))
WARN-ABOUT-SUSPICIOUS-RETURN
[15]>
(warning-upon-suspicious-return (values 1 2 3))
1 ;
2 ;
3
[16]>
(warning-upon-suspicious-return (values 1 2 3))
*** - EVAL: variable TROL has no value
1. Break [17]>
(defmacro warning-upon-suspicious-return (&body body)
(let ((bypass (gensym "BYPASS"))
(status (gensym "STATUS")))
`(block ,bypass
(multiple-value-prog1 (progn ,@body)
(let ((,status (query-status)))
(unless (eq ,status 'success)
(return-from ,bypass (warn-about-suspicious-
return ,status))))))))
WARNING-UPON-SUSPICIOUS-RETURN
1. Break [17]>
(defun warn-about-suspicious-return (status)
(let ((wrn (make-instance 'simple-warning
trol "status is ~A"
ts (list status))))
(warn wrn)
wrn))
WARN-ABOUT-SUSPICIOUS-RETURN
1. Break [17]>
(warning-upon-suspicious-return (values 1 2 3))
1 ;
2 ;
3
1. Break [17]>
(warning-upon-suspicious-return (values 1 2 3))
*** - EVAL: variable TROL has no value
2. Break [18]>
(defmacro warning-upon-suspicious-return (&body body)
(let ((bypass (gensym "BYPASS"))
(status (gensym "STATUS")))
`(block ,bypass
(multiple-value-prog1 (progn ,@body)
(let ((,status (query-status)))
(unless (eq ,status 'success)
(return-from ,bypass (test-warning ,status))))))))
WARNING-UPON-SUSPICIOUS-RETURN
2. Break [18]>
(defun test-warning (status)
(let ((warning (make-instance 'simple-warning
trol "status is ~A"
ts (list status))))
(warn warning)
warning))
TEST-WARNING
2. Break [18]>
(warning-upon-suspicious-return (values 1 2 3))
1 ;
2 ;
3
2. Break [18]>
(warning-upon-suspicious-return (values 1 2 3))
*** - EVAL: variable TROL has no value
3. Break [19]>
(defun test-warning (status)
(let ((wnr (make-instance 'simple-warning
:format-control "status is ~A"
:format-arguments (list status))))
(warn wnr)
wnr))
TEST-WARNING
3. Break [19]>
(warning-upon-suspicious-return (values 1 2 3))
1 ;
2 ;
3
3. Break [19]>
(warning-upon-suspicious-return (values 1 2 3))
WARNING:
status is T
#<SIMPLE-WARNING #x203BE529>
3. Break [19]>