From: Karol Skocik
Subject: problem with macro
Date: 
Message-ID: <1158176144.655037.170870@i42g2000cwa.googlegroups.com>
Hi guys,
  I am having problems with definition of one macro.
I have one macro (looks working) like this:

(defmacro with-buffered-messages ((&body messages) &body body)
  (let* ((total-size 0)
	 (forms '()))
    (loop :for (name time player-id entity-id . elements) :in messages
	  :for msg-size = (/ (+ +msg-size-bits+ +msg-id-bits+ (* 4 8)
+msg-player-id-bits+
				+msg-entity-id-bits+ (* (length elements) +msg-element-bits+)) 8)
	  :do (push `(,(msg-name (symbol-name name)) buffer ,total-size ,time
,player-id ,entity-id ,@elements) forms)
	  :do (incf total-size msg-size))
    (format t "////////// total-size = ~a~%" total-size)
    `(let ((buffer (make-msg-buffer ,total-size)))
      (declare (dynamic-extent buffer))
      ,@(nreverse forms)
      ,@body)))

it's use is for example:

(with-buffered-messages
    ((move time-1 player-1 entity-1 +dir-up+)
     (move time-2 player-2 entity-2 +dir-down+))
  (format t "////////// buffer is binded here to be send out~%")
  (send connection buffer))

and results into :

(LET ((BUFFER (MAKE-MSG-BUFFER 18)))
  (DECLARE (DYNAMIC-EXTENT BUFFER))
  (DO-MOVE-MSG BUFFER 0 TIME-1 PLAYER-1 ENTITY-1 +DIR-UP+)
  (DO-MOVE-MSG BUFFER 9 TIME-2 PLAYER-2 ENTITY-2 +DIR-DOWN+)
  (FORMAT T "////////// buffer is binded here to be send out~%")
  (SEND CONNECTION BUFFER))

So far, this seems ok to me. But this macro is not for normal use,
for application code I want to define macro based on this, which
could look like this: (but is wrong)

(defmacro with-msgs ((con) &body messages)
  (with-buffered-messages messages
    (send con buffer)))

It should be basically a direct use of with-buffered-messages in this
case and when used:

(with-msgs (con)
  (move time-1 player-1 entity-1 +dir-up+)
  (move time-2 player-2 entity-2 +dir-down+))

should expand into similar thing above (except the format call)

My problem is, that my definition of with-msgs is wrong and compilation
fails (SBCL 0.9.14) with :

; in: DEFMACRO WITH-MSGS
;     (TACTIX::WITH-BUFFERED-MESSAGES TACTIX::MESSAGES
;                                   (TACTIX::SEND TACTIX::CON
TACTIX::BUFFER))
;
; caught ERROR:
;   (in macroexpansion of (WITH-BUFFERED-MESSAGES MESSAGES #))
;   (hint: For more precise location, try *BREAK-ON-SIGNALS*.)
;   error while parsing arguments to DEFMACRO WITH-BUFFERED-MESSAGES:
;     bogus sublist MESSAGES to satisfy lambda-list (&BODY MESSAGES)

;     (DEFMACRO TACTIX::WITH-MSGS ((TACTIX::CON) &BODY
TACTIX::MESSAGES)
;     (TACTIX::WITH-BUFFERED-MESSAGES TACTIX::MESSAGES
;                                     (TACTIX::SEND TACTIX::CON
TACTIX::BUFFER)))
; --> EVAL-WHEN SB-C::%DEFMACRO FUNCTION LET*
; ==>
;   (LET* ((TACTIX::CON (CAR #:SUBLIST-2))
;          (TACTIX::MESSAGES (CDR (CDR #:WHOLE0))))
;     (BLOCK TACTIX::WITH-MSGS
;       (TACTIX::WITH-BUFFERED-MESSAGES TACTIX::MESSAGES
;                                       (TACTIX::SEND TACTIX::CON
;
TACTIX::BUFFER))))

which should be obvious to find but I can't ;(
After some break with Lisp hacking, all that uncommon stuff specific to
Lisp
just vanished :( need more experience

Can anybody enlight me please?

Karol Skocik

From: Ari Johnson
Subject: Re: problem with macro
Date: 
Message-ID: <m2psdz1pu1.fsf@nibbler.theari.com>
"Karol Skocik" <············@gmail.com> writes:

> Hi guys,
>   I am having problems with definition of one macro.
> I have one macro (looks working) like this:
>
> (defmacro with-buffered-messages ((&body messages) &body body)
>   (let* ((total-size 0)
> 	 (forms '()))
>     (loop :for (name time player-id entity-id . elements) :in messages
> 	  :for msg-size = (/ (+ +msg-size-bits+ +msg-id-bits+ (* 4 8)
> +msg-player-id-bits+
> 				+msg-entity-id-bits+ (* (length elements) +msg-element-bits+)) 8)
> 	  :do (push `(,(msg-name (symbol-name name)) buffer ,total-size ,time
> ,player-id ,entity-id ,@elements) forms)
> 	  :do (incf total-size msg-size))
>     (format t "////////// total-size = ~a~%" total-size)
>     `(let ((buffer (make-msg-buffer ,total-size)))
>       (declare (dynamic-extent buffer))
>       ,@(nreverse forms)
>       ,@body)))
>
> it's use is for example:
>
> (with-buffered-messages
>     ((move time-1 player-1 entity-1 +dir-up+)
>      (move time-2 player-2 entity-2 +dir-down+))
>   (format t "////////// buffer is binded here to be send out~%")
>   (send connection buffer))

Why is this a macro?  What's wrong with:
(let ((buffer
        (buffer-messages (list '(move time-1 player-1 entity-1 +dir-up+)
                               '(move time-2 player-2 entity-2 +dir-down+)))))
  (send connection buffer))

where BUFFER-MESSAGES is a regular function?

> and results into :
>
> (LET ((BUFFER (MAKE-MSG-BUFFER 18)))
>   (DECLARE (DYNAMIC-EXTENT BUFFER))
>   (DO-MOVE-MSG BUFFER 0 TIME-1 PLAYER-1 ENTITY-1 +DIR-UP+)
>   (DO-MOVE-MSG BUFFER 9 TIME-2 PLAYER-2 ENTITY-2 +DIR-DOWN+)
>   (FORMAT T "////////// buffer is binded here to be send out~%")
>   (SEND CONNECTION BUFFER))

Why the declaration?

> My problem is, that my definition of with-msgs is wrong and compilation
> fails (SBCL 0.9.14) with :
>
> ; in: DEFMACRO WITH-MSGS
> ;     (TACTIX::WITH-BUFFERED-MESSAGES TACTIX::MESSAGES
> ;                                   (TACTIX::SEND TACTIX::CON
> TACTIX::BUFFER))
> ;
> ; caught ERROR:
> ;   (in macroexpansion of (WITH-BUFFERED-MESSAGES MESSAGES #))
> ;   (hint: For more precise location, try *BREAK-ON-SIGNALS*.)
> ;   error while parsing arguments to DEFMACRO WITH-BUFFERED-MESSAGES:
> ;     bogus sublist MESSAGES to satisfy lambda-list (&BODY MESSAGES)

Your error is here the line directly above this sentence.  The error
is pretty self-explanatory.

> Can anybody enlight me please?

Yes.  On http://devpit.org/wiki/Lisp I wrote, "Knowing how and when to
use macros (and, conversely, how and when not to use them) is one of
the key skills in writing good Lisp code."  I think that your code
would be more readable if it were written without macros.
From: Karol Skocik
Subject: Re: problem with macro
Date: 
Message-ID: <1158184329.894606.224300@i3g2000cwc.googlegroups.com>
Ok,
  I think I have found it, nevermind.

This does the job:
(defmacro with-msgs ((con) &body messages)
  `(with-buffered-messages ,messages
    (send ,con buffer)))

when used like:

(with-msgs (connection)
  (move time1 player1 entity1 up))

I get :

(LET ((BUFFER (MAKE-MSG-BUFFER 9)))
  (DECLARE (DYNAMIC-EXTENT BUFFER))
  (DO-MOVE-MSG BUFFER 0 TIME1 PLAYER1 ENTITY1 UP)
  (SEND CONNECTION BUFFER))

Thanks,
  Karol

Ari Johnson wrote:
> "Karol Skocik" <············@gmail.com> writes:
>
> > Hi guys,
> >   I am having problems with definition of one macro.
> > I have one macro (looks working) like this:
> >
> > (defmacro with-buffered-messages ((&body messages) &body body)
> >   (let* ((total-size 0)
> > 	 (forms '()))
> >     (loop :for (name time player-id entity-id . elements) :in messages
> > 	  :for msg-size = (/ (+ +msg-size-bits+ +msg-id-bits+ (* 4 8)
> > +msg-player-id-bits+
> > 				+msg-entity-id-bits+ (* (length elements) +msg-element-bits+)) 8)
> > 	  :do (push `(,(msg-name (symbol-name name)) buffer ,total-size ,time
> > ,player-id ,entity-id ,@elements) forms)
> > 	  :do (incf total-size msg-size))
> >     (format t "////////// total-size = ~a~%" total-size)
> >     `(let ((buffer (make-msg-buffer ,total-size)))
> >       (declare (dynamic-extent buffer))
> >       ,@(nreverse forms)
> >       ,@body)))
> >
> > it's use is for example:
> >
> > (with-buffered-messages
> >     ((move time-1 player-1 entity-1 +dir-up+)
> >      (move time-2 player-2 entity-2 +dir-down+))
> >   (format t "////////// buffer is binded here to be send out~%")
> >   (send connection buffer))
>
> Why is this a macro?  What's wrong with:
> (let ((buffer
>         (buffer-messages (list '(move time-1 player-1 entity-1 +dir-up+)
>                                '(move time-2 player-2 entity-2 +dir-down+)))))
>   (send connection buffer))
>
> where BUFFER-MESSAGES is a regular function?
>
> > and results into :
> >
> > (LET ((BUFFER (MAKE-MSG-BUFFER 18)))
> >   (DECLARE (DYNAMIC-EXTENT BUFFER))
> >   (DO-MOVE-MSG BUFFER 0 TIME-1 PLAYER-1 ENTITY-1 +DIR-UP+)
> >   (DO-MOVE-MSG BUFFER 9 TIME-2 PLAYER-2 ENTITY-2 +DIR-DOWN+)
> >   (FORMAT T "////////// buffer is binded here to be send out~%")
> >   (SEND CONNECTION BUFFER))
>
> Why the declaration?
>
> > My problem is, that my definition of with-msgs is wrong and compilation
> > fails (SBCL 0.9.14) with :
> >
> > ; in: DEFMACRO WITH-MSGS
> > ;     (TACTIX::WITH-BUFFERED-MESSAGES TACTIX::MESSAGES
> > ;                                   (TACTIX::SEND TACTIX::CON
> > TACTIX::BUFFER))
> > ;
> > ; caught ERROR:
> > ;   (in macroexpansion of (WITH-BUFFERED-MESSAGES MESSAGES #))
> > ;   (hint: For more precise location, try *BREAK-ON-SIGNALS*.)
> > ;   error while parsing arguments to DEFMACRO WITH-BUFFERED-MESSAGES:
> > ;     bogus sublist MESSAGES to satisfy lambda-list (&BODY MESSAGES)
>
> Your error is here the line directly above this sentence.  The error
> is pretty self-explanatory.
>
> > Can anybody enlight me please?
>
> Yes.  On http://devpit.org/wiki/Lisp I wrote, "Knowing how and when to
> use macros (and, conversely, how and when not to use them) is one of
> the key skills in writing good Lisp code."  I think that your code
> would be more readable if it were written without macros.
From: Thomas A. Russ
Subject: Re: problem with macro
Date: 
Message-ID: <ymir6ye6z7j.fsf@sevak.isi.edu>
"Karol Skocik" <············@gmail.com> writes:

> Ok,
>   I think I have found it, nevermind.
> 
> This does the job:
> (defmacro with-msgs ((con) &body messages)
>   `(with-buffered-messages ,messages
>     (send ,con buffer)))
> 
> when used like:
> 
> (with-msgs (connection)
>   (move time1 player1 entity1 up))
> 
> I get :
> 
> (LET ((BUFFER (MAKE-MSG-BUFFER 9)))
>   (DECLARE (DYNAMIC-EXTENT BUFFER))
>   (DO-MOVE-MSG BUFFER 0 TIME1 PLAYER1 ENTITY1 UP)
>   (SEND CONNECTION BUFFER))

You should introduce a GENSYM instead of using the symbol BUFFER to name
the variable that the macro introduces.  Otherwise, this will shadow any
other uses of that variable.  It is generally considered dangerous
design to rely on the names of such introduced variables when you
combine or make reference to bound variables introduced by a macro
expansion.

It will cause certain code to mysteriously break.  Generally if you need
to be able to reference a variable bound by a macro, you should pass in
the symbol to use, so that it is clear in the code that this is
happening.

The expansion you have:
   (defmacro with-msgs ((con) &body messages)
     `(with-buffered-messages ,messages
         (send ,con buffer)))

requires that BUFFER be bound by WITH-BUFFERED-MESSAGES, but this would
then mean that any mention of the name BUFFER in the messages themselves
would be inadvertently captured.


-- 
Thomas A. Russ,  USC/Information Sciences Institute