4
votes

I need to create a class at run-time, possibly without resorting to eval. Knowing that the metaclass protocol is not fully standardized in Common-Lisp, after browsing through the The Common Lisp Object System MetaObject Protocol, I tried the following code to create a class, instantiate it, and set a slot value of the instance to a number:

(defparameter *my-class*
  (make-instance 'standard-class
                 :name 'my-class
                 :direct-slots '((:name x :readers (get-x) :writers ((setf get-x))))))

(defparameter *my-instance* (make-instance *my-class*))

(setf (get-x *my-instance*) 42) ;; => 42

Unfortunately this code works correctly on SBCL, but not on CCL, where the class creation seems to work, but the instance creation (make-instance *my-class*) causes the following error:

There is no applicable method for the generic function:
  #<STANDARD-GENERIC-FUNCTION INITIALIZE-INSTANCE #x30200002481F>
when called with arguments:
  (#<error printing CONS #x302001A9F6A3>
   [Condition of type CCL:NO-APPLICABLE-METHOD-EXISTS]

I tried looking at closer-mop package, that should hide the differences between the various implementations for the meta-object protocol, but I could not find any function or class useful to my scope.

So the question is: is there a portable way of creating a class and instantiating it at run time by using directly the metaclass level of CLOS?

2
With closer-mop, you could try ensure-class, or ensure-class-using-class. - coredump
CCL seems to work if you specify the direct superclasses too :direct-superclasses (list (find-class 'standard-object)) - jkiiski
Thanks @jkiiski, the problem is that I should have specified the superclass. If you post it as an answer, I will accept it. - Renzo
Thanks, @coredump, actually ensure-class does not work with my parameters, but works correctly if I add the direct-superclass standard-object (I was thinking it was a default, but maybe this is a default only for defclass). - Renzo
Oh because B->C is indeed not preserved. - philipxy

2 Answers

3
votes

CCL seems to require that you manually specify the direct superclasses as well.

(defparameter *my-class*
  (make-instance 'standard-class
                 :name 'my-class
                 :direct-slots '((:name x :readers (get-x) :writers ((setf get-x))))
                 :direct-superclasses (list (find-class 'standard-object))))
3
votes

Usually one would use ENSURE-CLASS to create a class. The purpose of ENSURE-CLASS is to be the functional equivalent of DEFCLASS. Minus special implementation-specific things DEFCLASS does - for example to support features of the development environment.

You can use MAKE-INSTANCE, but for example it won't register the class under its name. It also would not call any additional ENSURE-CLASS-USING-CLASS methods.

Since the default for the metaclass is standard-class, CCL should also compute a default for the direct superclasses, which it does not - unfortunately.

I would hope that closer-mop fixes those incompatibilities, but I haven't checked.

In CCL:

? (ensure-class 'my-class
                :direct-slots '((:name x
                                 :readers (get-x)
                                 :writers ((setf get-x))))
                :direct-superclasses (list (find-class 'standard-object)))
#<STANDARD-CLASS MY-CLASS>
? (find-class 'my-class)
#<STANDARD-CLASS MY-CLASS>
? (let ((foo (make-instance 'my-class)))
    (setf (get-x foo) 10)
    (incf (get-x foo) 32)
    (get-x foo))
42

LispWorks actually does it correctly. The metaclass defaults to standard-class and the direct superclass is then standard-object.

CL-USER 25 > (clos:ensure-class 'foobar
                 :direct-slots '((:name x
                                  :readers (get-x)
                                  :writers ((setf get-x)))))
#<STANDARD-CLASS FOOBAR 4020001713>

CL-USER 26 > (class-direct-superclasses *)
(#<STANDARD-CLASS STANDARD-OBJECT 40E018E313>)