4

我需要在运行时创建一个类,可能不求助于 eval。知道在 Common-Lisp 中元类协议没有完全标准化,浏览了The Common Lisp Object System MetaObject Protocol之后,我尝试了下面的代码来创建一个类,实例化它,并将实例的一个槽值设置为一个数字:

(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

不幸的是,此代码在 SBCL 上正常工作,但在 CCL 上却不能正常工作,其中类创建似乎工作,但实例创建(make-instance *my-class*)导致以下错误:

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]

我尝试查看close-mop包,它应该隐藏元对象协议的各种实现之间的差异,但我找不到任何对我的范围有用的函数或类。

所以问题是:有没有一种可移植的方式来创建一个类并在运行时通过直接使用 CLOS 的元类级别来实例化它?

4

2 回答 2

3

通常人们会用它ENSURE-CLASS来创建一个类。的目的ENSURE-CLASS是成为 的功能等价物DEFCLASS。减去特定于实现的特殊事物DEFCLASS- 例如支持开发环境的功能。

您可以使用MAKE-INSTANCE,但例如它不会在其名称下注册该类。它也不会调用任何其他ENSURE-CLASS-USING-CLASS方法。

由于元类的默认值是standard-class,因此 CCL 还应该计算直接超类的默认值,但不幸的是,它没有这样做。

我希望更紧密的拖把能解决这些不兼容问题,但我还没有检查过。

在 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 实际上做到了正确。元类默认为standard-class,直接超类为 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>)
于 2016-10-04T13:30:01.513 回答
3

CCL 似乎也要求您手动指定直接超类。

(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))))
于 2016-10-04T12:15:19.700 回答