Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

catching multiple values

6 views
Skip to first unread message

Tamas Papp

unread,
May 15, 2007, 2:43:27 PM5/15/07
to
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?

Thanks,

Tamas

--
Posted via a free Usenet account from http://www.teranews.com

Geoffrey Summerhayes

unread,
May 16, 2007, 2:45:40 PM5/16/07
to
On May 15, 2:43 pm, 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)))))
>
> 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 ((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


Geoffrey Summerhayes

unread,
May 16, 2007, 2:47:30 PM5/16/07
to
On May 15, 2:43 pm, 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)))))
>
> 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 ((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

Kaz Kylheku

unread,
May 16, 2007, 3:10:46 PM5/16/07
to
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)))))
>
> Now the fly in the ointment: it works fine if body returns a single value:

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.

nall...@gmail.com

unread,
May 16, 2007, 3:51:33 PM5/16/07
to
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)))))
>
> 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?

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)

Pascal Bourguignon

unread,
May 16, 2007, 4:19:00 PM5/16/07
to
Tamas Papp <tkp...@gmail.com> writes:

> 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.

Tamas Papp

unread,
May 16, 2007, 4:48:40 PM5/16/07
to
Pascal Bourguignon <p...@informatimago.com> writes:

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

Kent M Pitman

unread,
May 16, 2007, 8:53:55 PM5/16/07
to
nall...@gmail.com writes:

> 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.

Kent M Pitman

unread,
May 16, 2007, 8:57:21 PM5/16/07
to
Pascal Bourguignon <p...@informatimago.com> writes:

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

viper-2

unread,
May 23, 2007, 12:51:35 PM5/23/07
to


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]>

0 new messages