Index
x::const_ref and x::const_ptrcreate() - create reference-counted objectsbasex::ref or an x::ptr from thisx::refs and x::ptrsisa()
Nearly all objects in LibCXX are reference-counted objects,
similar to what std::shared_ptr
implements, but with notable differences as described here.
In this context, this is not the same kind of a “reference”
that's a part of the C++ language itself.
For clarity, “native references” will refer to traditional
C++ references, while the general usage of the term “reference
pointer”, or a
“reference”, refers to the construct described here.
Reference-counted objects do not get
accessed with an ordinary pointer, but by using a reference
pointer, an x::ref or an x::ptr.
A reference-counted object gets instantiated in dynamic scope using
a variadic create() function that forwards its
arguments to the object's constructor;
create()
instantiates the object in dynamic scope and
creates the first, initial reference pointer to the
newly-instantiated object.
Reference pointers look like ordinary pointers. They have
* and -> operators with
the expected behavior. Reference pointers may be freely copied and
passed around.
create() replaces new.
new does not get used to construct reference-
counter object in dynamic scope, and must not be used.
Similarly, delete does not get used, explicitly, with
reference-counted objects.
When the last reference to an object goes out of scope and longer exists,
the last reference pointer takes care of destroying the object
with a delete, and invoking any
destructor callbacks.
pthread_cancel(3) cannot be used in code that uses
LibCXX. pthread_cancel(3) terminates a thread without
unwinding the stack. The stack may contain references to
reference-counted objects, or other objects
whose destructors contain critical functionality.
Using pthread_cancel(3) will result in memory leaks,
deadlocks, and other unstable behavior. This is likely to be the case with
any C++-based process of non-trivial complexity,
not just LibCXX. The only safe way to forcibly terminate the thread is
by throwing an exception that unwinds the entire stack frame.
x::msgdispatcherObj
provides a convenient message-based thread design pattern which
supplies a stop() method that sends a message to the
running thread that causes it to throw an exception and terminate, in an
orderly manner.
This implementation of reference-counted objects is
similar to other similar implementations, notably
shared_ptr in the C++ library, however there are
fundamental differences. The key differences are:
A shared_ptr
contains a pointer to a small heap-allocated object
that tracks the reference count, and a second pointer to the
actual object:

LibCXX's reference-counted objects use a different approach
that's similar to Java's implementation of managed objects.
LibCXX's reference-counted objects are derived from the
x::obj superclass, which tracks
the reference
count.
There are two main reference pointer classes,
x::ref and x::ptr, holding a single pointer
to the object:

shared_ptr's
* and
-> operators
dereference a mutable object,
and dereferencing a nullptr is
undefined behavior.
x::ptr can also be a nullptr, but dereferencing a
nullptr is not undefined behavior:
an exception gets thrown, with defined semantics.
x::ref's * and ->
operators do not check for a nullptr
because x::ref cannot be a nullptr.
It always
refers to an object. This is enforced by contract.
The nullptr
gets checked when an x::ref gets assigned to.
x::ref is proven, by contract, never to contain a
nullptr; hence its
* and -> directly
dereferences the internal pointer to the underlying object.
An x::ref lvalue is convertible to an x::ptr lvalue without a temporary.
Converting an x::ptr to an x::ref checks for a
nullptr
at the time the conversion takes place, where an exception gets
thrown. An x::ptr may be passed to a function that takes an x::ref
argument. If the caller supplies a nullptr,
the exception gets thrown in the caller's context, for violating
the contract. The backtrace accurately points to the guilty party,
not the function, but its caller.
x::ptr and x::ref are analogous to natural C++ pointers and
references. Similarly, LibCXX defines an x::const_ptr and an
x::const_ref, that dereference to constant objects. This is
directly analogous to a std::iterator and a
std::const_iterator. Nothing similar is
available with a shared_ptr.
A shared_ptr<T> is convertible to a
shared_ptr<const T>, but doing that
for a function call requires the construction of a temporary.
An x::ptr lvalue converts to an x::const_ptr
lvalue without a temporary,
ditto for x::ref and an x::const_ref.
The primary benefit of using shared_ptr
is that it allows any class to acquire reference-counting
semantics without modification. Standard C++ library classes, such as
various stream objects, can be easily wrapped into a
reference-counted framework.
However, there are several disadvantages to
shared_ptr that x::ref and x::ptr aim to address.
Each reference-counted object requires allocating another small
object, the reference counter, from the heap. Over the long term,
this is more likely to increase
heap fragmentation, and heap usage, especially
in long running applications, as reference-counted objects get
created and destroyed. make_shared
partially mitigates this, however the end result has to be
compatible with the stock shared_ptr and that
introduces additional behind the scenes complexity.
There is no straightforward way to prove, by contract, that a
shared_ptr instance is not a
nullptr.
It's not possible to construct a shared_ptr
from this, unless the class explicitly
inherits from an enable_shared_from_this
superclass. But this cancels out
shared_ptr primary benefit of wrapping any
arbitrary class. Furthermore, this approach becomes somewhat
difficult when multiple inheritance gets involved.
LibCXX stores the reference count in each object's superclass,
x::obj which is
directly accessible by any subclass. A new x::ref or an x::ptr
gets trivially constructed from this,
without much fanfare.
This creates a new reference, and increments
the object's reference count.
Furthermore, since the counter is a part of the object, it does not
need to be allocated separately, on the heap. The only drawback is that,
unlike with shared_ptr, it's not possible to
implement reference-counted semantics for an arbitrary class. It is
necessary to declare a subclass that multiply
inherits from that class, and
from x::obj.
x::refs and x::ptrs work with multiple inheritance without any special
effort. Each reference-counted class
virtually inherits from x::obj, which
automatically does the right thing when multiple reference-counted
classes get inherited from.