From: awhite
Subject: cl-sdl and sdl:rect macro writing
Date: 
Message-ID: <47ddf3d8$0$6843$5a62ac22@per-qv1-newsreader-01.iinet.net.au>
Hello,

to help teach myself lisp, I'm trying to write a tile-based game using 
the SDL library. I had a good look at lispbuilder-sdl, and while it's 
very lispy, I'm trying to use the older cl-sdl.

In the C version, libSDL uses the concept of SDL_Rect, which is usually 
used by pointer to a structure containing 4 int values (x, y, w, h). 

CL-SDL exposes this via a couple of macros over cffi.

The examples given in cl-sdl seem to only use one SDL_Rect (called 
sdl:rect) each, cleaning up the resources at the end. I tend to use lots 
of sdl:rect structures for fast blitting.

I'm hoping for a bit of help on macros - I've come up with:

(defmacro create-rect (x y w h)
  (let ((rect (gensym)))
    `(sgum:with-foreign-objects ((,rect sdl:rect))
       (setf (sdl:rect-x ,rect) ,x (sdl:rect-y ,rect) ,y 
             (sdl:rect-w ,rect) ,w (sdl:rect-h ,rect) ,h)
       ,rect)))

which seems to work, but leaks like a sieve, as after use you need to:

(when (rect)
  (sgum:free-foreign-object rect))

to clean up resources.
I'd like a macro to automate all of this for me.
Something to work like:

(with-rects ((srcrect x y w h) (dstrect x2 y2 w2 h2))
  ; do my funky thang with srcrect and dstrect
)

I can't figure out the syntax. I'm pretty sure I need (unwind-protect) to 
run the free-foreign-object over the rects at the end of the form, and a 
(symbol-macrolet) to handle naming each rect. But I'm still getting to 
grips with common lisp, and can't figure out how to write macros properly.

Can anyone help?

Thanks,

Adam

From: Ken Tilton
Subject: Re: cl-sdl and sdl:rect macro writing
Date: 
Message-ID: <47de2524$0$5651$607ed4bc@cv.net>
awhite wrote:
> Hello,
> 
> to help teach myself lisp, I'm trying to write a tile-based game using 
> the SDL library. I had a good look at lispbuilder-sdl, and while it's 
> very lispy, I'm trying to use the older cl-sdl.
> 
> In the C version, libSDL uses the concept of SDL_Rect, which is usually 
> used by pointer to a structure containing 4 int values (x, y, w, h). 
> 
> CL-SDL exposes this via a couple of macros over cffi.
> 
> The examples given in cl-sdl seem to only use one SDL_Rect (called 
> sdl:rect) each, cleaning up the resources at the end. I tend to use lots 
> of sdl:rect structures for fast blitting.
> 
> I'm hoping for a bit of help on macros - I've come up with:
> 
> (defmacro create-rect (x y w h)
>   (let ((rect (gensym)))
>     `(sgum:with-foreign-objects ((,rect sdl:rect))
>        (setf (sdl:rect-x ,rect) ,x (sdl:rect-y ,rect) ,y 
>              (sdl:rect-w ,rect) ,w (sdl:rect-h ,rect) ,h)
>        ,rect)))
> 
> which seems to work, but leaks like a sieve, as after use you need to:
> 
> (when (rect)
>   (sgum:free-foreign-object rect))

Just a small point: why would rect be nil? Or are you just playing it 
safe against the chance some client does their own free (and then sets 
the variable to nil? No big deal.

> 
> to clean up resources.
> I'd like a macro to automate all of this for me.
> Something to work like:
> 
> (with-rects ((srcrect x y w h) (dstrect x2 y2 w2 h2))
>   ; do my funky thang with srcrect and dstrect
> )
> 
> I can't figure out the syntax. I'm pretty sure I need (unwind-protect) to 
> run the free-foreign-object over the rects at the end of the form, and a 
> (symbol-macrolet) to handle naming each rect. But I'm still getting to 
> grips with common lisp, and can't figure out how to write macros properly.

You have two good resources on-line, Seibel's PCL and Graham's On Lisp. 
Seibel talks about first coding up the code you would like to write 
(which you have done here):

 > (with-rects ((srcrect x y w h) (dstrect x2 y2 w2 h2))
 >   ; do my funky thang with srcrect and dstrect
 > )

...and then the expansion you would like to see:

   (with-foreign-objects ((srcrect sdl:rect)...(dstrect sdl:rect))
     (unwind-protect
	(progn
             (setf (sdl:rect-x srcrect) ,x (sdl:rect-y srcrect) ,y
                   (sdl:rect-w srcrect) ,w (sdl:rect-h srcrect) ,h)
              <same for dstrect>
	    ,@body)
       (when g1 (free-foreign-object g1))
       ...
       <same up to gn>))

...and then write the code to get from the first to the second.

One point: by sticking srcrect and dstrect into the w-f-objects object 
list you will get the lexical variables you are after, so symbol-macros 
are not needed. The rest is just an exercise in iterating over the rect 
specifications (eg, (srcrect 1 2 3 4)) three times, one to tell 
w-f-objects which f-objects to allocate, once to setf the slots of each 
rect, and once to generate the cleanup forms (suggest you tackle them 
one at a time testing via (macroexpand '(with-sdl-rects ....)).

Here's a start (to be combined with the above):

(defmacro with-sdl-rects (rect-specifications &body body)
   `(with-foreign-objects ,(mapcar (lambda (rspec)
					`(,(car rspec) sdl:rect))
				rect-specifications)
      ...setfs...
      ...body...
      ...cleanups...))

kenny

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: awhite
Subject: Re: cl-sdl and sdl:rect macro writing
Date: 
Message-ID: <47df0053$0$6869$5a62ac22@per-qv1-newsreader-01.iinet.net.au>
On Mon, 17 Mar 2008 04:00:35 -0400, Ken Tilton wrote:


>> (when (rect)
>>   (sgum:free-foreign-object rect))
> 
> Just a small point: why would rect be nil? Or are you just playing it
> safe against the chance some client does their own free (and then sets
> the variable to nil? No big deal.

Actually I was just copying one of the examples. The example code did it 
because *rect* was declared special, and was initially bound to nil.
You're right - I didn't need to check.

> You have two good resources on-line, Seibel's PCL and Graham's On Lisp.
> Seibel talks about first coding up the code you would like to write
> (which you have done here):

I have a copy of PCL - great book. I obviously need to re-read the macro 
chapters again.


> (defmacro with-sdl-rects (rect-specifications &body body)
>    `(with-foreign-objects ,(mapcar (lambda (rspec)
> 					`(,(car rspec) sdl:rect))
> 				rect-specifications)
>       ...setfs...
>       ...body...
>       ...cleanups...))
> 

Thanks Ken, that's exactly the bit I was struggling with ,(mapcar ....) 
and the double backtick interpolation. Even though I think I logically 
"get" macros, it's still hard getting out of the C++ mindset.

A
From: Ken Tilton
Subject: Re: cl-sdl and sdl:rect macro writing
Date: 
Message-ID: <47df15c1$0$25034$607ed4bc@cv.net>
awhite wrote:
> On Mon, 17 Mar 2008 04:00:35 -0400, Ken Tilton wrote:
> 
> 
> 
>>>(when (rect)
>>>  (sgum:free-foreign-object rect))
>>
>>Just a small point: why would rect be nil? Or are you just playing it
>>safe against the chance some client does their own free (and then sets
>>the variable to nil? No big deal.
> 
> 
> Actually I was just copying one of the examples. The example code did it 
> because *rect* was declared special, and was initially bound to nil.
> You're right - I didn't need to check.
> 
> 
>>You have two good resources on-line, Seibel's PCL and Graham's On Lisp.
>>Seibel talks about first coding up the code you would like to write
>>(which you have done here):
> 
> 
> I have a copy of PCL - great book. I obviously need to re-read the macro 
> chapters again.

Remember to develop the macro in baby steps, using macroexpand to check 
your progress until you can compile and run the expansion meaningfully.

> 
> 
> 
>>(defmacro with-sdl-rects (rect-specifications &body body)
>>   `(with-foreign-objects ,(mapcar (lambda (rspec)
>>					`(,(car rspec) sdl:rect))
>>				rect-specifications)
>>      ...setfs...
>>      ...body...
>>      ...cleanups...))
>>
> 
> 
> Thanks Ken, that's exactly the bit I was struggling with ,(mapcar ....) 
> and the double backtick interpolation.

It's definitely strange thinking partly in runtime (the code being 
generated) and partly at macroexpansion time (juggling the source code 
seen by the macro function) and bouncing back and forth between them, 
and it definitely takes time to get fluent, but it is not /too/ bad a 
learning curve and now I can write pretty astonishing macros and have 
them work the first time, so how hard can it be? I'm not all that smart.

You never want to use a macro for no reason, but there are many places 
boilerplate can be made to disappear, so (my point) it helps if you 
write a lot of macros.

kenny

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Pascal J. Bourguignon
Subject: Re: cl-sdl and sdl:rect macro writing
Date: 
Message-ID: <7cmyoxr8bg.fsf@pbourguignon.anevia.com>
awhite <·······@iinet.net.au> writes:

> Hello,
>
> to help teach myself lisp, I'm trying to write a tile-based game using 
> the SDL library. I had a good look at lispbuilder-sdl, and while it's 
> very lispy, I'm trying to use the older cl-sdl.
>
> In the C version, libSDL uses the concept of SDL_Rect, which is usually 
> used by pointer to a structure containing 4 int values (x, y, w, h). 
>
> CL-SDL exposes this via a couple of macros over cffi.
>
> The examples given in cl-sdl seem to only use one SDL_Rect (called 
> sdl:rect) each, cleaning up the resources at the end. I tend to use lots 
> of sdl:rect structures for fast blitting.
>
> I'm hoping for a bit of help on macros - I've come up with:
>
> (defmacro create-rect (x y w h)
>   (let ((rect (gensym)))
>     `(sgum:with-foreign-objects ((,rect sdl:rect))
>        (setf (sdl:rect-x ,rect) ,x (sdl:rect-y ,rect) ,y 
>              (sdl:rect-w ,rect) ,w (sdl:rect-h ,rect) ,h)
>        ,rect)))
>
> which seems to work, but leaks like a sieve, as after use you need to:
>
> (when (rect)
>   (sgum:free-foreign-object rect))
>
> to clean up resources.

I doubt it.  Otherwise, why would WITH-FOREIGN-OBJECTS be named
WITH-FOREIGN-OBJECTS instead of ALLOCATE-FOREIGN-OBJECTS?

Check the documentation of WITH-FOREIGN-OBJECTS, or read the macroexpansion:

(macroexpand '(sgum:with-foreign-objects ((rect sdl:rect)) (print rect)))


> I'd like a macro to automate all of this for me.
> Something to work like:
>
> (with-rects ((srcrect x y w h) (dstrect x2 y2 w2 h2))
>   ; do my funky thang with srcrect and dstrect
> )
>
> I can't figure out the syntax. I'm pretty sure I need (unwind-protect) to 
> run the free-foreign-object over the rects at the end of the form, and a 
> (symbol-macrolet) to handle naming each rect. But I'm still getting to 
> grips with common lisp, and can't figure out how to write macros properly.
>
> Can anyone help?

To write a macro you need to know:
1- what you want it to expand to,
2- what will change from one expansion to the other,
3- what syntax you want it to expand from.

So, if you want to build forms such as:

(sgum:with-foreign-objects ((R1 sdl:rect)...)
    (setf (sdl:rect-x R1) R1-X
          (sdl:rect-y R1) R1-Y
          (sdl:rect-w R1) R1-W
          (sdl:rect-h R1) R1-H
          ...)
     SOME-BODY-FORM...)

with the parts in uppercase changing, and if you want to write:

(with-rects ((R1 R1-X R1-Y R1-W R1-H)...) SOME-BODY-FORM...) 

to start with, then you can directly build your defmacro:

(defmacro WITH-RECTS ((&rest RECT-CLAUSES) &body SOME-BODY-FORMS)
    `(sgum:with-foreign-objects
          ,(mapcar (lambda (rect-clause)
                      (destructuring-bind (r x y w h) rect-clause
                            `(,r sdl:rect))) RECT-CLAUSES)
        (setf ,@(mapcan (lambda (rect-clause)
                           (destructuring-bind (r x y w h) rect-clause
                               (list `(sdl:rect-x ,r) x
                                     `(sdl:rect-y ,r) y
                                     `(sdl:rect-w ,r) w
                                     `(sdl:rect-h ,r) h))) RECT-CLAUSES))
         ,@SOME-BODY-FORMS))

            

Now, assuming you don't have a WITH-FOREIGN-OBJECTS, but an
ALLOCATE-FOREIGN-OBJECT, you may want to build forms such as:

(let ((R1 (SGUM:ALLOCATE-FOREIGN-OBJECT sdl:rect))
      ...)
 (unwind-protect
     (progn
        (setf (sdl:rect-x R1) R1-X
              (sdl:rect-y R1) R1-Y
              (sdl:rect-w R1) R1-W
              (sdl:rect-h R1) R1-H
              ...)
         SOME-BODY-FORM...)
   (sgum:free-foreign-object r1)
   ...))

Now, if we can get an error in allocate-foreign-object, we may want to
free the previously allocated ones. So perhaps we'd rather have:

(let ((R1 (SGUM:ALLOCATE-FOREIGN-OBJECT sdl:rect)))
 (unwind-protect
     (progn
        (setf (sdl:rect-x R1) R1-X
              (sdl:rect-y R1) R1-Y
              (sdl:rect-w R1) R1-W
              (sdl:rect-h R1) R1-H)
        (let ((R2  (SGUM:ALLOCATE-FOREIGN-OBJECT sdl:rect)))
           (unwind-protect
               (progn
                    (setf (sdl:rect-x R2) R2-X
                          (sdl:rect-y R2) R2-Y
                          (sdl:rect-w R2) R2-W
                          (sdl:rect-h R2) R2-H)
                  ...
                     SOME-BODY-FORM...)
                  (sgum:free-foreign-object r2))))
   (sgum:free-foreign-object r1)))

which actually will make a simplier, but recursive macro:


(defmacro WITH-RECTS (((rectvar x y w h) &rest RECT-CLAUSES) &body SOME-BODY-FORMS)
  `(let ((,rectvar (sgum:allocate-foreign-object sdl:rect)))
     (unwind-protect                             
          (progn
              (setf (sdl:rect-x ,rectvar) ,x
                    (sdl:rect-y ,rectvar) ,y
                    (sdl:rect-w ,rectvar) ,w
                    (sdl:rect-h ,rectvar) ,h)
              ,(if rect-clauses
                  `(with-rects ,rect-clauses ,@some-body-forms)
                  `(progn ,@some-body-forms)))
        (sgum:free-foreign-object ,rectvar))))






C/USER[12]> (macroexpand '(with-rects ((r1 1 1 10 100) (r2 2 2 20 200) (r3 3 3 30 300)) (draw r1) (draw (inter r2 r3))))
(LET ((R1 (SGUM:ALLOCATE-FOREIGN-OBJECT SDL:RECT)))
 (UNWIND-PROTECT
  (PROGN (SETF (SDL:RECT-X R1) 1 (SDL:RECT-Y R1) 1 (SDL:RECT-W R1) 10 (SDL:RECT-H R1) 100)
   (WITH-RECTS ((R2 2 2 20 200) (R3 3 3 30 300)) (DRAW R1) (DRAW (INTER R2 R3))))
  (SGUM:FREE-FOREIGN-OBJECT R1))) ;
T
C/USER[13]> (ext:expand-form '(with-rects ((r1 1 1 10 100) (r2 2 2 20 200) (r3 3 3 30 300)) (draw r1) (draw (inter r2 r3))))
(LET ((R1 . #1=((SGUM:ALLOCATE-FOREIGN-OBJECT SDL:RECT))))
 (UNWIND-PROTECT
  (PROGN
   (LET NIL
    (PROGN (LET* ((#2=#:TEMP-10553 R1) (#3=#:NEW-10552 1)) (FUNCALL #'(SETF SDL:RECT-X) #3# #2#))
     (LET* ((#4=#:TEMP-10555 R1) (#5=#:NEW-10554 1)) (FUNCALL #'(SETF SDL:RECT-Y) #5# #4#))
     (LET* ((#6=#:TEMP-10557 R1) (#7=#:NEW-10556 10)) (FUNCALL #'(SETF SDL:RECT-W) #7# #6#))
     (LET* ((#8=#:TEMP-10559 R1) (#9=#:NEW-10558 100)) (FUNCALL #'(SETF SDL:RECT-H) #9# #8#))))
   (LET ((R2 . #1#))
    (UNWIND-PROTECT
     (PROGN
      (LET NIL
       (PROGN (LET* ((#10=#:TEMP-10561 R2) (#11=#:NEW-10560 2)) (FUNCALL #'(SETF SDL:RECT-X) #11# #10#))
        (LET* ((#12=#:TEMP-10563 R2) (#13=#:NEW-10562 2)) (FUNCALL #'(SETF SDL:RECT-Y) #13# #12#))
        (LET* ((#14=#:TEMP-10565 R2) (#15=#:NEW-10564 20)) (FUNCALL #'(SETF SDL:RECT-W) #15# #14#))
        (LET* ((#16=#:TEMP-10567 R2) (#17=#:NEW-10566 200)) (FUNCALL #'(SETF SDL:RECT-H) #17# #16#))))
      (LET ((R3 . #1#))
       (UNWIND-PROTECT
        (PROGN
         (LET NIL
          (PROGN (LET* ((#18=#:TEMP-10569 R3) (#19=#:NEW-10568 3)) (FUNCALL #'(SETF SDL:RECT-X) #19# #18#))
           (LET* ((#20=#:TEMP-10571 R3) (#21=#:NEW-10570 3)) (FUNCALL #'(SETF SDL:RECT-Y) #21# #20#))
           (LET* ((#22=#:TEMP-10573 R3) (#23=#:NEW-10572 30)) (FUNCALL #'(SETF SDL:RECT-W) #23# #22#))
           (LET* ((#24=#:TEMP-10575 R3) (#25=#:NEW-10574 300)) (FUNCALL #'(SETF SDL:RECT-H) #25# #24#))))
         (PROGN (DRAW R1) (DRAW (INTER R2 R3))))
        (SGUM:FREE-FOREIGN-OBJECT R3))))
     (SGUM:FREE-FOREIGN-OBJECT R2))))
  (SGUM:FREE-FOREIGN-OBJECT R1))) ;
T
C/USER[14]> 

     
-- 
__Pascal Bourguignon__
From: Luke Crook
Subject: Re: cl-sdl and sdl:rect macro writing
Date: 
Message-ID: <5f94ac40-e729-457c-a0d2-721158a83107@d4g2000prg.googlegroups.com>
On Mar 16, 9:30 pm, awhite <·······@iinet.net.au> wrote:
> Hello,
>
> to help teach myself lisp, I'm trying to write a tile-based game using
> the SDL library. I had a good look at lispbuilder-sdl, and while it's
> very lispy, I'm trying to use the older cl-sdl.

Funny. I wrote lispbuilder-sdl to teach myself Lisp.

- Luke