Search code examples
macroslispcommon-lispclos

How to write (simple) macro?


I need to write a macro (with-hooks (monster method who what) &body body) for a game I'm writing. Monster is a CLOS object, method and who are strings and what is a function (#' notation). The macroexpansion would be something to the effect of

(add-hook monster method who what)
,@body
(remove-hook monster method who)

I have absolutely no idea how to write such a macro, and I would appreciate some help. I have the creepy feeling that this is easy and I'm a bit ignorant.


Solution

  • I'd write it like this:

    (defmacro with-hooks ((monster method who what) &body body)
      (let ((monster-var (gensym))
            (method-var (gensym))
            (who-var (gensym))
            (what-var (gensym)))
        `(let ((,monster-var ,monster) ; dummy comment
               (,method-var ,method)
               (,who-var ,who)
               (,what-var ,what))
            (add-hook ,monster-var ,method-var ,who-var ,what-var)
            (unwind-protect
               (progn ,@body)
              (remove-hook ,monster-var ,method-var ,who-var)))))
    

    Some notes:

    1. something-vars are used to ensure that expressions for monster, method, who, what are evaluated only once (because these expressions are referenced multiple times in macro body) and in left-to-right order.
    2. gensyms are used to ensure that variables have guaranteed unique names
    3. unwind-protect is used to ensure that remove-hook is called even in case of non-local exits (e.g., stack unwind due to exception being thrown).