create() - create reference-counted objects

#include <x/ref.H>
#include <x/ptr.H>

class buttonObj : virtual public x::obj {

    buttonObj(int width, int height);
    ~buttonObj() noexcept;

// ...


typedef x::ref<buttonObj> button;
typedef x::ptr<buttonObj> buttonptr;

button quit(button::create(100, 100));

An x::ref and an x::ptr defines a member typedef named base. This is a typedef to a class with static methods, or other members that are provided by the x::ref or the x::ptr, to supplement the class * and -> method operators. A x::ref's and x::ptr's create() is actually a call to base::objfactory<T>::create(), with T being the x::ref or a x::ptr type that's calling it, and is the expected return type from create(). By default, base gets typedef-ed to x::ptrrefBase, which defines the objfactory template class parameter with a static create() method that constructs a new object, for the x::ref or the x::ptr.

x::ref's and x::ptr's create() method forwards its arguments to base::objfactory<T>::create(). The default implementation, x::ptrrefBase::objfactory<T>::create(), constructs the object on the heap with new, and forwards the arguments to the object's constructor. In nearly all cases, it's sufficient to define a constructor in the reference-counted subclass of x::obj, but it's possible to define a custom base with a create() that does something else.

Reference-counted objects do not get explicitly deleted, as this is done by the reference-counting templates. create() removes all the news too. With create(), x::ref, and x::ptr, an application that uses LIBCXX will not have any news and deletes. Outside of create(), LIBCXX itself has a grand total of two explicit news, to allocate two low-level objects before most of its classes get initialized.

Range-based iteration on reference-counted objects

It can be convenient to use a reference-counted object that begin() and end(), x::dirwalk, as a range iterator:

x::dirwalk etcdir=x::dirwalk::create("/etc");

for (auto config: *etcdir)
    // ...

Note the need to dereference etcdir since, for all practical purposes, it's a pointer. begin() and end() gets implemented in the object it points to.


This shorter alternative seems intuitive, but it's actually ill-formed:

for (auto config: *x::dirwalk::create("/etc"))
    // ...

The range iterator takes an rvalue reference. The reference from create() goes out of scope and gets destroyed, but the rvalue reference remains in scope, and gets invoked for the begin() and the end(). C++11 defines range iteration as equivalent to (roughly speaking).

auto && range=*x::dirwalk::create("/etc");

for (auto iter=range.begin(); auto != range.end(); ++iter)
    // ...

x::dirwalk::create() instantiates a reference-counted object. * dereferences, and leaves it as an rvalue reference. The reference then gets destroyed, at the end of the sequence point, and as it's the only reference to the reference-counted object, it gets destroyed. Meanwhile, the rvalue reference, to the destroyed object remains, producing ill-formed results.

Rule of thumb to follow: when using the * operator to dereference a reference-counted object for a container-based range operation, the * operator should operate on an lvalue, and never an rvalue.