[plt-scheme] An interface wrapper using macros and interface->method-names

From: David Stigant (dstigant at houston.rr.com)
Date: Wed Jun 2 18:05:51 EDT 2004

I've been trying to write a macro which will take an interface and create a class which forwards all calls to the interface's methods on to another implementation of that interface.  For example, given this interface:

(define ifoo<%> (interface () foo bar baz))

I want my macro

(wrap ifoo<%>)

to expand to this:

(class* object% (ifoo<%>)
   (init-field (impl void))
      (foo (lambda args (send impl foo . args)))
      (bar (lambda args (send impl bar . args)))
      (baz (lambda args (send impl baz . args)))
      (set-impl (lambda (i) (set! impl i))))
   (super-instantiate ())

The first three methods of this class simply forward requests to the ifoo<%> interface onto impl.  The set-impl method allows me to change the implementation under the covers without any of the clients of my object knowing or having to update their references to the new impl.

Now, what I have started with is this:

(define-syntax (wrap stx)
   (syntax-case stx ()
       ((wrap iface<%> (method ...))
        #`(class* object% (iface<%>)
             (init-field (impl void))
                 (set-impl (lambda (i) (set! impl i)))
                 (method (lambda args (send impl method . args))) ...)
             (super-instantiate ())))))

Which works great (ie it produces the correct class) except  that I have to invoke the macro like this:

(wrap ifoo<%> (foo bar baz))

I don't want to have to list all the method names out everytime I use this wrapper.  What if I change the method names in the interface?  Now I have to go back and change all the invocations of the macro as well.  Lousy.  So I wanted to use the interface->method-names function to generate the list of methods and then pass those into the macro automatically.  For reference, interface->method-names works like this:

(interface->method-names ifoo<%>)   ==> '(foo bar baz)

So it seems like I ought to be able to do this:

(wrap ifoo<%> (interface->method-names ifoo<%>))

However, this yeilds a class with methods "interface->method-names" and "ifoo<%>" instead of "foo" "bar" and "baz".  Or rather, doesn't produce anything since that class doesn't properly implement ifoo<%>

What I've come up with is this hack:

(define make-wrapper
   (lambda (iface<%>)
      (eval  ` (wrap iface<%> ,(interface->method-names iface<%>)))))

(define foo-wrapper% (make-wrapper ifoo<%>))

But my skin crawls whenever I use eval.  I've tried this out in local and global contexts and it seems to work, but is it really kosher and is there a cleaner way to accomplish what I want?  Is there a way to combine the make-wrapper function into the macro definition?

Any help is appreciated.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20040602/cce3437d/attachment.html>

Posted on the users mailing list.