References

About

References are used to manage allocated objects and to reference-count these objects. For example, when all references to a given object are deleted, the object itself will be deleted. References are based on the maxon::BaseRef template.

The following reference types are available:

Using these reference templates, non-intrusive reference counting can be applied to any allocated object. A reference based alias implements a "Create()" function that allows the easy allocation of a new, reference counted instance.

For safe access to a reference from multiple threads see maxon::ThreadSafeRef.

Pointer

maxon::Pointer is handling an ordinary C++ pointer.

UniqueRef

A maxon::UniqueRef stores a pointer to a given object. It will delete the object when itself is destroyed. Only one maxon::UniqueRef can have the ownership of a given object at a time.

Note
A maxon::UniqueRef object can be moved (using std::move()) but not copied.
The const-ness of the maxon::UniqueRef object can be used to control access to the referenced object.
// This example defines an alias for an UniqueRef managing a SimpleElement.
// The alias is used to create a new, managed instance of SimpleElement.
// define alias
using ElementRef = maxon::UniqueRef<SimpleElement>;
{
// allocate a new element
ElementRef ref = ElementRef::Create() iferr_return;
// set data
ref->_data = 123;
// read data
DiagnosticOutput("Data: @", ref->_data);
// when the scope is left, UniqueRef and the allocated element are destroyed
}
// This example shows how a UniqueRef is used to manage a newly created object.
// If the function fails, the UniqueRef will destroy the allocated object.
// If the ownership could be transferred successfully, UniqueRef will release it.
//----------------------------------------------------------------------------------------
// Creates a SimpleElement object and adds it to the given PointerArray
// @param[in] elements The array to store the new object.
// @return maxon::OK on success.
//----------------------------------------------------------------------------------------
{
// allocate new element
// UniqueRef takes ownership
// element is handed over to the PointerArray which will take ownership
// if this operations fails, UniqueRef makes sure the allocated element is freed
elements.AppendPtr(ref) iferr_return;
// the PointerArray has taken ownership
// UniqueRef must release ownership
ref.Disconnect();
return maxon::OK;
}

maxon::AutoMem is an alias for maxon::UniqueRef handling raw memory maxon::RawMem. See also Memory Allocation.

// This example uses AutoMem to manage the allocated memory.
{
// new elements are allocated, AutoMem takes ownership
// data is filled
for (maxon::Int i = 0; i < count; ++i)
data[i]._data = i;
// when the scope is left, AutoMem will delete the allocated memory
}

StrongRef, StrongCOWRef and WeakRef

One or many maxon::StrongRef objects can reference a given object in memory. When all strong references to that object are destroyed, the object will be deleted.

// This example stores the newly created object in a StrongRef.
// The StrongRef takes ownership and deletes the object when itself is destroyed.
// allocate object
SimpleElement* const obj = NewObj(SimpleElement) iferr_return;
// set data
obj->_data = 123;
{
// strong reference takes ownership
// print data
DiagnosticOutput("Data: @", ref->_data);
// when the scope is left, the StrongRef is destroyed
// and with it the referenced object
}
// now "obj" has been freed
// This example defines an alias for a StrongRef managing a SimpleElement.
// This alias can be used to create a new instance of that SimpleElement that
// is owned by a StrongRef. A copy of the original StrongRef is then created.
// The copy is pointing to the same data as the original.
// defining an alias
{
// allocate the object
const SimpleRef original = SimpleRef::Create() iferr_return;
original->_data = 123;
{
// create a copy of the StrongRef
const SimpleRef copy = original;
// edit original data
original->_data = 456;
// "original" and "copy" are pointing to the same data
DiagnosticOutput("@ : @", original->_data, copy->_data);
// when the scope is left, SimpleRef "copy" is destroyed
}
// when the scope is left, SimpleRef "original" is destroyed
// and the allocated object with it
}

A maxon::StrongRef can also manage raw memory:

// This example manages the newly allocated memory
// with a StrongRef that frees the memory at the end.
{
// allocate memory
// data is filled
for (maxon::Int i = 0; i < count; ++i)
data[i]._data = i;
// when the scope is left, the allocated memory is deleted
}

A maxon::StrongCOWRef is the same as maxon::StrongRef but it supports copy-on-write techniques. As long as no reference is trying to modify the referenced object, all references point to the same object. When a reference wants to modify the object, the object is copied and the reference used for modification has ownership of that new object.

// This example shows how a StrongCOWRef is used to manage objects.
// The StrongCOWRef takes ownership of a newly created object.
// Then a copy of the original StrongCOWRef is created. As long
// as the data does not change, both the original and the copy
// point to the same data. Only when the copy should be changed
// using MakeWritable() will a new object be created.
// defining an alias
using SimpleCOWRef = maxon::StrongCOWRef<SimpleElement>;
{
// allocates the object
SimpleElement* const data = NewObj(SimpleElement) iferr_return;
// set data
data->_data = 100;
// StrongCOWRef takes ownership
const SimpleCOWRef original(data);
{
// create a copy of the StrongCOWRef
SimpleCOWRef copy = original;
// edit original data
data->_data = 200;
// "original" and "copy" are pointing to the same data
DiagnosticOutput("@ : @", original->_data, copy->_data);
// make copy writeable; a new instance will be allocated, owned by "copy"
SimpleElement& copyData = copy.MakeWritable() iferr_return;
copyData._data = 300;
// original and copy will now store different data
DiagnosticOutput("@ : @", original->_data, copy->_data);
// when the scope is left, StrongCOWRef "copy" is destroyed
}
// when the scope is left, StrongCOWRef "original" is destroyed
// and the allocated object with it
}

A maxon::WeakRef points to an object owned by a maxon::StrongRef. When there are no more strong references, the object is deleted and the maxon::WeakRef returns a null reference.

// This example uses WeakRefs to access data managed with a StrongRef.
// define alias
// allocate an element
SimpleElement* const element = NewObj(SimpleElement) iferr_return;
element->_data = 123;
// prepare WeakRef
{
// StrongRef takes ownership
const SimpleRef strongRef(element);
// set WeakRef
weakRef = strongRef;
// WeakRef is valid
if (weakRef)
{
// use WeakRef to access data
const SimpleRef data = weakRef;
DiagnosticOutput("Data: @", data->_data);
}
// when the scope is left, SimpleRef gets destroyed
// and the allocated element will be freed
}
// now WeakRef points to nothing
if (!weakRef)
DiagnosticOutput("WeakRef invalid");

Classes

Instances of custom classes can be managed with maxon::BaseRef based reference types. If it is needed to modify the reference-counting behaviour one can implement the following functions in the custom class:

  • AddReference(): Is called when a reference to the object is added.
  • RemoveReference(): Is called when a reference to the object is removed.
  • CreateStrongReference(): Is called when a strong reference is added.
  • AddWeakReference(): Is called when a weak reference to the object is added.
  • InitialReference(): Sets the initial reference to a newly allocated object.

This is typically used for:

  • debugging reference handling
  • reference counting with alien memory management
  • synchronization of a reference with a background object
// This example shows a simple class that implements AddReference() and RemoveReference().
// Within these functions the class manages its own reference counting.
//----------------------------------------------------------------------------------------
// A simple, reference counted object.
//----------------------------------------------------------------------------------------
class ReferencedElement
{
public:
//----------------------------------------------------------------------------------------
// Constructor.
//----------------------------------------------------------------------------------------
MAXON_IMPLICIT ReferencedElement()
{
_data = 0;
}
//----------------------------------------------------------------------------------------
// Destructor.
//----------------------------------------------------------------------------------------
~ReferencedElement()
{
_data = 0;
}
//----------------------------------------------------------------------------------------
// Adds a reference to this instance.
//----------------------------------------------------------------------------------------
void AddReference() const
{
DiagnosticOutput("Add reference");
// increase reference count
}
//----------------------------------------------------------------------------------------
// Removes a reference pointing to this instance.
// The object will be deleted if this was the last reference.
//----------------------------------------------------------------------------------------
void RemoveReference() const
{
DiagnosticOutput("Remove reference");
// decrease reference count
{
// if the last strong reference was released,
// delete the object
}
}
maxon::Int _data;
};
// This example uses the class ReferencedElement that manages its own reference counting.
ReferencedElement* const element = NewObj(ReferencedElement) iferr_return;
element->_data = 123;
{
// store in StrongRef, AddReference() will be called
const maxon::StrongRef<ReferencedElement> firstRef(element);
{
// store in second StrongRef, AddReference() will be called
const maxon::StrongRef<ReferencedElement> secondRef = firstRef;
// when the scope is left, "secondRef" is deleted and
// RemoveReference() is called
}
// when the scope is left, "firstRef" is deleted,
// RemoveReference() is called and the element freed
}

Further Reading