The CSpace garbage collector requires every object
in the CSpace
which implements interface IObject to visit
all outgoing pointers to other objects in the CSpace
in its implementation of the VisitObjects()
member function.
The insertion operator makes is easy for a class to implement VisitObjects()
. Typically it's as
simple as "listing" all the data members. For example
$class Automobile isa IAutomobile
{
void VisitObjects(IObjectVisitor& v) const
{
v << engine << wheels;
BaseClass::VisitObjects(v);
}
ptr<IEngine> engine;
xvector<ptr<IWheel> > wheels;
};
The framework allows the standard C++ library std::pair
, std::vector
,
std::deque
, std::list
, std::set
and std::map
members to be directly "visited".
The template implementations of the insertion operator simply recurse into the elements.
Given that std::map
elements are std::pair
objects and it is common
for only one of the elements to be an IObject
pointer, the framework provides
"do nothing" implementations for visiting the basic types such as bool
,
char
, short
, int
, long
, float
,
double
and xstring
.
An important type of pointer is the pref<T>
which represents a pointer to a
persistent object. These should also be visited.
If the programmer forgets to implement VisitObjects()
, or fails to visit all
outgoing IObject
pointers then there is a risk that objects that are in
fact reachable will be collected during the next sweep phase.
This will typically lead to access violations.
Non-IObject classes can be written that contain IObject
pointers. Consider the following example:
struct Node
{
ptr<IObject> left;
ptr<IObject> right;
};
In these cases it is best to write an insertion operator like this:
inline IObjectVisitor& operator<<(IObjectVisitor& v, const Node& node)
{
v << node.left << node.right;
return v;
}
Consider that Node
is stored within another non-IObject class called Page
.
It is easy to write the insertion operation for Page
as follows
struct Page
{
xvector<Node> nodes;
};
inline IObjectVisitor& operator<<(IObjectVisitor& v, const Page& page)
{
v << page.nodes;
return v;
}
Eventually non-IObject classes must be reachable from an IObject
(or else they are garbage and can
be collected). At this point they will be visited in the normal way - i.e. from an implementation of
VisitObjects()
.
$class X isa IObject
{
void VisitObjects(IObjectVisitor& v) const
{
v << page;
}
Page page;
};