On 27 Dec 2010, at 04:37, Sam Steingold wrote:
Hi, I want to write a macro which would expand to a defclass + some code which uses the resulting class object using mop. e.g. (untested), (defmacro deffoo (class slots) `(progn (defclass ,class () ,slots) (defun foo (x) (list ,@(mapcar (lambda (ds) `(,(car (slot-definition-readers ds)) x)) (class-direct-slots (find-class class)))))))
which should expand (deffoo bar ((a :reader bar-a) (b :reader bar-b))) to something like this: (progn (defclass bar () ((a :reader bar-a) (b :reader bar-b))) (defun foo (x) (list (bar-a x) (bar-b x))))
Alas, CLASS is not defined at read time when (class-direct-slots (find-class class)) and (slot-definition-readers ds) want to be evaluated.
So, how do I do this?
Just to be complete, I insist that the 'right' way to do this is to do this at runtime. Here is a sketch of how to do this in such a way that you don't suffer that much from additional runtime overheads:
(defclass foo-dependent () ((function-symbol :initarg :function-symbol))
(defmethod update-dependent ((class standard-class) (dependent foo-dependent) &rest initargs) (declare (ignore initargs)) (setf (symbol-function (slot-value dependent 'function-symbol)) (compile nil `(lambda (x) (list ,@(loop for slot in (class-direct-slots class) for name = (slot-definition-name slot) collect `(slot-value x ',name)))))))
(defmacro deffoo (class slots) `(progn (defclass ,class () ,slots) (defun foo (x)) (add-dependent (find-class ',class) (make-instance 'foo-dependent :function-symbol 'foo)) (reinitialize-instance (find-class ',class)) ',class))
[Untested.]
Other variations are possible, like using a method on finalize-inheritance instead of update-dependent, if you can afford to have a separate metaclass for this. If you are sure that the class doesn't change at runtime, you can still generate the body of the foo function at loadtime / runtime, after the complete class exists.
Of course, this adds to some amount of load-time overhead, which may not be acceptable under certain circumstances.
Pascal