"Frederick C. Gibson, Architect" <Freder...@gibson-design.com> writes:
> Is there a way in CLOS to get a list of all instances from the class object Not by default. You can, in an implementation that supports a MOP,
> of which the instances are members?
use a user-defined metaclass that upon instantiation logs the instances.
You can also, on a special-case basis for a specific class, do this
without metaclasses by just adding an after method on INITIALIZE-INSTANCE
for the class that pushes the element onto some list.
(defvar *the-foos* '())
(defmethod initialize-instance :after ((self foo) &key)
(push self *the-foos*))
There is a problem with doing it that way (which is why metclasses are
a better way) because if you subclass FOO you won't get a new
variable. You might think you could solve this by doing:
(defclass foo ()
((all-instances :allocation :class :accessor all-instances :initform '())))
and then pushing onto (all-instances self), which would be the class
slot. But the problem is that you'll get the same bug because when you
subclass a class with a slot that uses :allocation :class, you do not
get a new slot for each subclass--the subclasses all share the same
slot. [The Dylan lanuage has something that would in CL be called
:allocation :each-subclass, which is what you really want and CL does
SOMETIMES having the shared variable is actually right, but often it is not
so you have to think about it in advance.
I have sometimes worked around this by doing
(defvar *all-instances* nil)
(defmethod initialize-instance :after ((self instance-recording-class) &key)
(push self (getf *all-instances* (class-of self))))
(defmethod all-instances ((self instance-recording-class))
(getf *all-instances* (class-of self)))
or something like that. I think if you search in Deja News, or maybe
even look in the FAQ, you can probably find one or more worked examples
of doing something like this with the MOP stuff, that I'm not super-familiar
with mostly because I've always preferred to write really portable code
that doesn't require the MOP.
Incidentally, whether you use the MOP or not, there is an issue that you
can end up really defeating garbage collection if you keep track of all
the instances, so be very careful with anything like this and use it only
if you really have to. I think some implementations may have tools that
sweep the address space looking for instances exactly to avoid the gc locking
problem, but that's not a basic CL operation, and implementations are not
required to include enough information in the image to actually be able to
recover such... (Such tools would necessarily cons, though, upon
demand, and so you'd risk holding a pointer to things that want to GC even
if you use one of them. )
You could also use weak hash tables (which most implementations provide
but are not standard) to help avoid the gc locking problem for instances.