如何更改类的元类

时间:2016-08-07 07:27:23

标签: common-lisp sbcl clos

这种情况一次又一次地发生在我身上:我定义了这个类并且忘记了我希望它可以使用它,或者它是,例如,Gtk小部件类,因此需要声明它的元类。但是,一旦定义了,SBCL就不会让我改变元类(即使没有这个类的实例)。例如,评估

(defclass foo ()
  ((slot-a)))

然后添加元类并重新评估:

(defclass foo ()
  ((slot-a))
  (:metaclass gobject:gobject-class))

导致错误:

Cannot CHANGE-CLASS objects into CLASS metaobjects.
   [Condition of type SB-PCL::METAOBJECT-INITIALIZATION-VIOLATION]
See also:
  The Art of the Metaobject Protocol, CLASS [:initialization]

不幸的是,我没有“元对象协议的艺术”副本来检查它的内容。现在,我能想出的唯一方法就是重启lisp,这可能会造成很大的破坏。

由于我很快意识到错误,我不介意通过删除它来完全避开已定义的类。问题:

  • 如果我已经创建了该类的实例,是否有办法找到它们以使它们无效并让它们进行GCed?
  • 如何删除课程?像fmakunbound这样的功能。

1 个答案:

答案 0 :(得分:10)

  

不幸的是,我没有一份“元对象协议的艺术”副本来检查它的内容。

即使我推荐阅读本书,您也可以在线查找一些信息。请参阅示例ENSURE-CLASS-USING-CLASS

  

如何删除课程?

您可以使用(SETF FIND-CLASS)

(setf (find-class 'foo) nil)

或者,您可以使用花式粘液检查员。在指向类的名称时调用slime-inspect-defintion。然后,您将看到该名称。选择它时,检查命名类的符号。然后,您可以看到类似的内容:

It names the class FOO [remove]

如果FOO只命名一个类,你可以使用更大的锤子:

(unintern 'foo)
  

如果我已经创建了类的实例,有没有办法找到它们来使它们无效并让它们获得GCed?

不,只有GC具有全局视图,并且出于实际原因,它通常不会向后引用关于谁引用特定对象(以及如何) 1 < / SUP>。 除非您引入自己的(弱)哈希表来存储它们,否则没有类的所有实例的全局记录。但是如果你保留了所有实例的记录,你可以CHANGE-CLASS。例如,您定义:

(defclass garbage () ())

...您的对象中任何以前保存的引用都将被释放,GC有机会处理引用的实例对象。当另一个对象引用垃圾实例时,您可以更新它。您可以将旧类的实例更改为新类(名称相同,但类对象不同),而不是使用&#39; garbage。 另请注意,CHANGE-CLASS是一个通用函数。

<强> 1 即可。实现可能提供堆walker 。请参阅示例Heap walkers in Allegro CL