Referencing

SAMSON has a referencing system that is used in place of regular C++ pointers and references to hold addresses of SAMSON objects, in particular data graph nodes.

Except in very specific cases, typically not unrelated to voodoo and dark magic, SAMSON Elements should always reference SAMSON objects, especially data graph nodes, thanks to the provided reference owners.

Two properties of reference owners make them particulary useful for efficiently managing reference targets and controlling their lifetime:

Definitions

The following two definitions are used:

  • A reference target is an object that can be referenced (pointed to) by another object.
  • A reference owner is an object that can reference (point to) another object.

Reference targets

Reference targets are objects of classes that derive from the SBCReferenceTarget class. Reference targets maintain a list of reference owners that reference them. In SAMSON, many classes are reference targets. In particular, all data graph nodes are reference targets.

Reference owners

In SAMSON, three types of reference owners are defined:

SAMSON Pointers

SAMSON pointers replace C++ pointers when referencing SAMSON objects that are reference targets (i.e. derive, directly or indirectly, from SBCReferenceTarget).

SAMSON Pointer indexes

SAMSON pointer indexers are reference owners that may index a collection of reference targets in the same way that SBCContainerIndexer may be used to index collections of values.

Indexing is particularly useful when reference targets must be associated to a collection of data. Assume for example that a selection of atoms should be stored by a SAMSON Element, and that an array of SBPosition3 should be managed in order to store e.g. the atoms positions. The atoms may have been extracted from the current selection:

SBPointer<SBDocument> document = SAMSON::getActiveDocument();
const SBPointerIndex<SBNode>* selection = document->getSelectedNodes();
std::vector<SBNode*> atomVector;
SBNode::getNodes(selection, atomVector, SBNode::Atom);
SBPosition3* positionArray = new SBPosition3[atomVector.size()];

In order to keep persistent references to the atoms and benefit from the properties of reference owners, pointers may be stored in a SAMSON pointer indexer:

SBPointerIndex<SBAtom> atomIndex; // typically in a header file
std::vector<SBNode*>::iterator i;
for (i = atomVector.begin(); i!= atomVector.end(); i++) {
atomIndex.addReferenceTarget(*i);
}

The atomIndex indexer maps the n atoms to integers 0, 1, ... n-1. It may be used to go from indices to atoms:

SBAtom* atom = atomIndex.getReferenceTarget(17); // atom with index 17

and to retrieve the unique index that atomIndex associates to an atom:

unsigned int index = atomIndex.getIndex(atom); // index = 17

The mapping from reference targets to unsigned ints permitted by SAMSON pointer indexers is particularly useful to manage data associated to reference targets. Assume a structural event signals that an atom position changed. The SAMSON pointer indexer may be used to retrieve which element of the positionArray array should be updated:

// get the atom address
SBAtom* atom = static_cast<SBAtom*>(event->getNode());
// make sure the atom is indexed
// and save its position should it be the cases
unsigned int index;
if (atomIndex.getIndex(atom, index)) positionArray[index] = atom->getPosition();
}

SAMSON Pointer lists

SAMSON pointer lists are reference owners that may list a collection of reference targets in the same way that SBCContainerList may be used to index lists of values. When a reference target is deleted, all links of the SAMSON pointer list that reference the reference target are automatically removed from the list.

Properties of reference owners

As noted in the introduction, reference owners have two main properties:

Collective ownership

Reference owners collectively own the reference target they point to: when the last reference owner stops referencing the reference target, the reference target is deleted.

Assume for example an atom is pointed to by two SAMSON pointers:

SBPointer<SBAtom> atomPointer1 = new SBAtom();
SBPointer<SBAtom> atomPointer2 = atomPointer1;

If the first pointer becomes 0:

atomPointer1 = 0;

the atom is still pointed to by the second pointer, and nothing happens. However, as soon as the second pointer stops referencing the atom:

atomPointer2 = 0;

the atom is deleted.

Note that this is a general mechanism: ownership is shared by all reference owners that reference a reference target. For example, if an atom is pointed to by a SAMSON pointer, a SAMSON pointer indexer and a SAMSON pointer list:

SBPointer<SBAtom> atomPointer = new SBAtom();
SBPointerIndex<SBAtom> atomIndex;
atomIndex.addReferenceTarget(atomPointer());
SBPointerList<SBAtom> atomList;
atomList.addReferenceTarget(atomPointer());

then the following instructions lead to the atom being deleted:

atomIndex.removeReferenceTarget(atomPointer());
atomList.removeReferenceTarget(atomPointer());
atomPointer = 0; // the atom is deleted, since it is not referenced anymore

Note that a reference owner stops referencing when it becomes destructed. Assume for example that a reference to an atom is made on the stack (for example through a SAMSON pointer declared in a local scope), although the atom is created on the heap:

void garbageCollectionExample() {
SBPointer<SBAtom> atom = new SBAtom(SBElement::Carbon);
}

When the function garbageCollectionExample terminates, the SAMSON pointer atom is destroyed. As a result, it stops referencing the atom object (the instance of the SBAtom class). Since this reference was the only one, the reference target (the atom object) is deleted.

Notification of reference target destructions

Reference owners are notified when a reference target they reference is deleted. For example, if a SAMSON pointer stores the address of an atom:

SBPointer<SBAtom> atomPointer = new SBAtom();

and the atom is deleted at some point, then atomPointer will become invalid (i.e. atomPointer.isValid() will return false). In effect, the SAMSON pointer will behave like a NULL (0) C++ pointer. For example, a comparison to 0 (atomPointer == 0) will return true.

More generally, when a reference target is deleted, all reference owners that reference the reference target are notified:

Notifying reference owners that a reference target is deleted has a linear complexity in the number of reference owners, and does not depend on the number of other reference targets the reference owners may point to.

When should reference owners be used?

Reference owners should be used when a reference to a reference target should persist, and there is a risk that the reference target may be deleted by another reference owner.

The need for reference owners may depend on the type of SAMSON class being implemented. For example, an importer typically does not need to store references to the data graph nodes it adds to the data graph. However, an app may need to store references to the data graph nodes it works with, in case the user or other SAMSON Elements may delete them.

Using reference owners

Referencing a reference target

Referencing a reference target depends on the type of reference owner being used.

Deleting a reference target

In order to delete a reference target, the deleteReferenceTarget function should be used. In particular, the C++ delete operator should not be used.

In SAMSON, reference targets store a list of reference owners that reference them. When a reference target is deleted, all reference owners are notified and their reference to the reference target is removed.

Assume for example that an atom is pointed to by two SAMSON pointers:

SBPointer<SBAtom> atom1 = new SBAtom(SBElement::Carbon);
SBPointer<SBAtom> atom2 = atom1;

Then, if the atom is deleted through e.g. atom1:

atom1.deleteReferenceTarget();

then atom2 is warned and stops referencing the deleted object.

In many cases, however, it may be unnecessary to explicitly delete a reference target, since SAMSON performs a basic form of garbage collection. If, for example, an atom is constructed and referenced by a single SAMSON pointer in a given scope, then the atom is automatically deleted when the SAMSON pointer goes out of scope, since the SAMSON pointer was the only reference owner that was holding a reference to the atom:

{
SBPointer<SBAtom> atom = new SBAtom(SBElement::Carbon);
} // here, the atom is automatically deleted