pbowyer
pbowyer

Reputation: 21

Why does this macro work one way and not the other?

This is a macro defined in "clx-user-callable.lisp" I'm trying to use.

(in-package :clx-gui)

(defmacro get-callback-wrapper (callback)
  (declare (ignorable callback))
  (let* ((func-name (gensym))
         (wrapper-name (intern (format nil "WRAPPER-~a" func-name) )))
    `(defun ,wrapper-name (caller-instance)
       (funcall ,callback) ;; User callbacks wont have arguments
       (closemenu caller-instance))))

I call this macro in this manner and it works properly.

(in-package :clx-gui-test-app)

(create-user-menuitem "MyUserMenu" "MyEntryDialog"
                      (get-callback-wrapper 'my-callback))

(create-user-menuitem "MyUserMenu" "MyChoiceDialog"
                      (get-callback-wrapper 'my-callback2))

(create-user-menuitem "MyUserMenu" "MyMessageDialog"
                      (get-callback-wrapper 'my-callback3))

If I change the code to use the macro this way, by passing the symbol name of the callback to a function that calls the macro, it does not return different wrapper functions, but always returns the same wrapper function. The function that calls the macro is in the same file and package as the macro definition.

(in-package :clx-gui-test-app)
(create-user-menuitem "MyUserMenu" "MyEntryDialog" 'my-callback)
(create-user-menuitem "MyUserMenu" "MyChoiceDialog" 'my-callback2)
(create-user-menuitem "MyUserMenu" "MyMessageDialog" 'my-callback3)

I have tried adding the package to the macro definition, but that doesn't help.

(wrapper-name (intern (format nil "WRAPPER-~a" func-name)
                      (symbol-package callback) )))

What am I doing incorrectly?

I'm working with SBCL-1.0.57 and Slime.

Upvotes: 2

Views: 178

Answers (1)

Clayton Stanley
Clayton Stanley

Reputation: 7784

CL-USER>
(defparameter foo1 (gensym))
FOO1
CL-USER> 
foo1
#:G4619
CL-USER> 
(defparameter foo2 '#:G4619)
FOO2
CL-USER> 
foo2
#:G4619
CL-USER> 
(eq foo1 foo2)
NIL
CL-USER> 
~           

Or another fun exercise:

(defmacro make-fun ()  
  `(defun ,(intern (format nil "WRAPPER-~a" (gensym))) ()
     'bar))

CL-USER> 
(make-fun)
WRAPPER-G4726
CL-USER> 
(make-fun)
WRAPPER-G4730
CL-USER> 
(make-fun)
WRAPPER-G4734
CL-USER> 
(make-fun)
WRAPPER-G4738
CL-USER> 
(defun WRAPPER-G4745 ()
  'foo)
WRAPPER-G4745
CL-USER> 
(make-fun)
WRAPPER-G4745
CL-USER> (wrapper-G4745)
BAR
CL-USER>

Oh man, we just wrote over that function!

If you want to notate a gensym with some sort of prefix name, do it in the gensym call (as an optional argument). But all of this is just an exercise, b/c I would still just use lambda in the OP problem.

Here's an alternative implementation that is (IMO) simpler, and should work for your needs:

(defun get-callback-wrapper (callback)
  (lambda (caller-instance)
    (funcall callback) 
    (closemenu caller-instance)))

This generates the lexical closure that I think you're after.

Upvotes: 1

Related Questions