6.3 Advice for macros and methods

As well as attaching advice to ordinary functions, it may also be attached to macros and methods.

In the case of a macro, advice is linked to the macro's expansion function, and so any before or after advice receives a copy of the arguments given to this expansion function (normally the macro call form and an environment). A simple example:

CL-USER 45 > (defmacro twice (b) `(+ ,b ,b))
CL-USER 46 > (defadvice
         (twice before-twice :before)
         (call-form env)
         (format t
         "~%Twice with environment ~A and call-form  ~A"
              env call-form))
CL-USER 47 > (twice 3)
Twice with environment NIL and call-form (TWICE 3) 

Note that the advice is invoked when the macro's expansion function is used. So if the macro is present within a function that is being compiled, then the advice is invoked during compilation of that function (and not when that function is finally used).

In the case of a method, the call to defadvice must also specify precisely to which method the advice belongs. A generic function may have several methods, so the call to defadvice includes a list of classes. This must correspond exactly to the parameter specializers of one of the methods for that generic function, and it is to that method that the advice is attached. For example:

CL-USER 45 > (progn
             (defclass animal ()
              (genus habitat description
              (food-type :accessor eats)
              (happiness :accessor how-happy)
              (eaten :accessor eaten :initform nil)))
             (defclass cat (animal)
              ((food-type :initform 'fish)))
             (defclass elephant (animal)
              (memory (food-type :initform 'hay)))
             (defmethod feed ((animal animal))
              (let ((food (eats animal)))
             (push food (eaten animal))
             (format t "~%Feeding ~A with ~A" animal
            (defmethod feed ((animal cat))
             (let ((food (eats animal)))
            (push food (eaten animal))
            (push 'milk (eaten animal))
            (format t "~%Feeding cat ~A with ~A and ~A"
                 animal food 'milk)))
            (defvar *cat* (make-instance 'cat))
            (defvar *nellie* (make-instance 'elephant)))
CL-USER 46 > (feed *cat*)
Feeding cat #<CAT 6f35d4> with FISH and MILK
CL-USER 47 > (feed *nellie*)
Feeding #<ELEPHANT 71e7bc> with HAY 
CL-USER 48 > (defadvice
             ((method feed (animal))
              after-feed :after)
             (format t "~%~A has eaten ~A"
                       animal (eaten animal)))
CL-USER 49 > (defadvice
             ((method feed (cat))
              before-feed :before)
             (format t "~%Stroking ~A" animal)
             (setf (how-happy animal) 'high))
CL-USER 50 > (feed *cat*)
Stroking #<CAT 6f35d4> 
Feeding cat #<CAT 6f35d4> with FISH and MILK 
CL-USER 51 > (feed *nellie*)
Feeding #<ELEPHANT 71eb7c> with HAY 
#<ELEPHANT 71eb7c> has eaten (HAY HAY)

LispWorks User Guide - 8 Apr 2005