如何在 Common-Lisp CLOS 中以 运行 的时间可移植地创建一个 class
How to portably create a class at run-time in Common-Lisp CLOS
我需要在 运行 时创建一个 class,可能不需要求助于 eval。知道metaclass协议在Common-Lisp中没有完全标准化,在浏览了The Common Lisp Object System MetaObject Protocol之后,我尝试了下面的代码来创建一个class,实例化它,并设置一个slot实例的值到一个数字:
(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 上不行,其中 class 创建似乎可以工作,但实例创建 (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]
我试着查看 closer-mop 包,它应该隐藏元对象协议的各种实现之间的差异,但我找不到任何函数或 class 对我的范围有用。
所以问题是:是否有一种可移植的方法来创建 class 并通过直接使用 CLOS 的元 class 级别在 运行 时间实例化它?
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))))
通常会使用 ENSURE-CLASS
创建一个 class。 ENSURE-CLASS
的目的是成为 DEFCLASS
的功能等效项。减去特定于特定实现的东西 DEFCLASS
所做的 - 例如支持开发环境的功能。
您可以使用MAKE-INSTANCE
,但例如它不会在其名称下注册class。它也不会调用任何额外的 ENSURE-CLASS-USING-CLASS
方法。
由于 metaclass 的默认值是 standard-class
,CCL 还应该计算 direct superclasses 的默认值,它没有 - 不幸的是。
我希望 closer-mop 修复这些不兼容问题,但我还没有检查。
在 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 实际上是正确的。 metaclass 默认为 standard-class
而直接 superclass 则为 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>)
我需要在 运行 时创建一个 class,可能不需要求助于 eval。知道metaclass协议在Common-Lisp中没有完全标准化,在浏览了The Common Lisp Object System MetaObject Protocol之后,我尝试了下面的代码来创建一个class,实例化它,并设置一个slot实例的值到一个数字:
(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 上不行,其中 class 创建似乎可以工作,但实例创建 (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]
我试着查看 closer-mop 包,它应该隐藏元对象协议的各种实现之间的差异,但我找不到任何函数或 class 对我的范围有用。
所以问题是:是否有一种可移植的方法来创建 class 并通过直接使用 CLOS 的元 class 级别在 运行 时间实例化它?
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))))
通常会使用 ENSURE-CLASS
创建一个 class。 ENSURE-CLASS
的目的是成为 DEFCLASS
的功能等效项。减去特定于特定实现的东西 DEFCLASS
所做的 - 例如支持开发环境的功能。
您可以使用MAKE-INSTANCE
,但例如它不会在其名称下注册class。它也不会调用任何额外的 ENSURE-CLASS-USING-CLASS
方法。
由于 metaclass 的默认值是 standard-class
,CCL 还应该计算 direct superclasses 的默认值,它没有 - 不幸的是。
我希望 closer-mop 修复这些不兼容问题,但我还没有检查。
在 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 实际上是正确的。 metaclass 默认为 standard-class
而直接 superclass 则为 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>)