How to change class's metaclass
Asked Answered
D

1

12

This happens to me time and again: I define the class and forget that I wanted it funcallable or it is, say, Gtk widget class, thus it's metaclass needs to be stated. Once it is defined, however, SBCL doesn't let me change me the metaclass (even if there is no instance of this class). For example, evaluating

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

and then adding a metaclass and re-evaluating:

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

results in error:

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]

Unfortunately I don't have a copy of The Art of the Metaobject Protocol to check what it says. For now the only way I could figure out is to restart lisp, which can be quite disruptive.

Since I realise the error soon enough, I don't mind dodging the defined class completely by removing it. Questions:

  • If I have created instances of the class, is there a way to find them to nullify them and get them GCed?
  • How to remove the class? Something like fmakunbound for functions.
Doting answered 7/8, 2016 at 7:27 Comment(0)
D
12

Unfortunately I don't have a copy of The Art of the Metaobject Protocol to check what it says.

Even though I recommend reading the book, you can find some information online. See for example ENSURE-CLASS-USING-CLASS.

How to remove the class?

You can use (SETF FIND-CLASS):

(setf (find-class 'foo) nil)

Or, you can use the fancy slime inspector. You call slime-inspect-defintion while pointing the name of the class. Then, you'll see the name. When you select it, you inspect the symbol naming your class. Then, you can see something like:

It names the class FOO [remove]

Provided FOO only names a class, you can use the bigger hammer:

(unintern 'foo)

If I have created instances of the class, is there a way to find them to nullify them and get them GCed?

No, only the GC has the global view, and for practical reasons, it doesn't generally keep backward references about who references a particular object (and how)1. There is no global record of all instances of a class unless you introduce your own (weak) hash-table to store them. But if you have kept a record of all of your instances, you can CHANGE-CLASS them. For example, you define:

(defclass garbage () ())

... any previously held reference in your object will be released and the GC has an opportunity to dispose of objects your instances where referencing. And when another object reference an instance of 'garbage, you can update it. Instead of using 'garbage, you could probably change instances of old classes to your new class (the name is the same, but the class object is different). Note also that CHANGE-CLASS is a generic function.


1. Implementations might offer heap walkers. See for example Heap walkers in Allegro CL.

Delay answered 7/8, 2016 at 8:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.