cl-sdl and sdl:rect macro writing

cl-sdl and sdl:rect macro writing

Post by awhit » Tue, 18 Mar 2008 13:30:16


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
 
 
 

cl-sdl and sdl:rect macro writing

Post by Ken Tilto » Tue, 18 Mar 2008 17:00:35


awhite wrote:

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.


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

 
 
 

cl-sdl and sdl:rect macro writing

Post by pjb » Tue, 18 Mar 2008 18:47:15

white < XXXX@XXXXX.COM > writes:


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



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)
 
 
 

cl-sdl and sdl:rect macro writing

Post by awhit » Wed, 19 Mar 2008 08:35:47


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.


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



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
 
 
 

cl-sdl and sdl:rect macro writing

Post by Ken Tilto » Wed, 19 Mar 2008 10:07:13


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


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://www.yqcomputer.com/
http://www.yqcomputer.com/

"In the morning, hear the Way;
in the evening, die content!"
-- Confucius
 
 
 

cl-sdl and sdl:rect macro writing

Post by Luke Croo » Thu, 20 Mar 2008 04:03:54


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

- Luke
 
 
 

cl-sdl and sdl:rect macro writing

Post by Andreas Da » Thu, 27 Mar 2008 06:20:09

awhite < XXXX@XXXXX.COM > writes:


Are there any specific reason you use cl-sdl? lispbuilder-sdl is
working very well and the mailing list is very friendly and helpful. Is
being "lispy" a bad thing?

/Andreas

--
A: Because it fouls the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?