Reference and pointer traits

	  #include <x/refptr_traits.H>

	  class someClassObj;
	  class someBase;

	  typedef x::ref<someClassObj, someBase> some_ref;

	  typedef x::refptr_traits<some_ref>::ref_t ref_t;
	  typedef x::refptr_traits<some_ref>::ptr_t ptr_t;
	  typedef x::refptr_traits<some_ref>::const_ref_t const_ref_t;
	  typedef x::refptr_traits<some_ref>::const_ptr_t const_ptr_t;


	  typedef x::ref_cast<some_ref> ref_cast_t;
	  typedef x::ptr_cast<some_ref> ptr_cast_t;

These template classes take an x::ref or an x::ptr and replace it with another reference or a pointer to the same object (and base class). The x::refptr_traits, x::ref_cast, and x::ptr_cast templates are specialized for a x::ref, x::ptr, x::const_ref, and a x::const_ptr.

x::refptr_traits is a class with four member types: ref_t, ptr_t, const_ref_t, and const_ptr_t. These types are a x::ref, x::ptr, x::const_ref, and a x::const_ptr to the same class (and base class) as the template parameter.

A x::ref_cast of a x::ref or a x::ptr results in a x::ref to the same class (and base class). A x::ref_cast of a x::const_ref or a x::const_ptr results in a x::const_ref to the same class (and base class). A x::ptr_cast of a x::ref or a x::ptr results in a x::ptr to the same class (and base class). A x::ptr_cast of a x::const_ref or a x::const_ptr results in a x::const_ptr to the same class (and base class).

Constructing a collection of references

Using x::ref, where possible, is better than x::ptr. x::ref proves, by contract, that it's a not a nullptr. x::ref's only disadvantage is that it's not possible to declare an x::ref without an actual object to reference. x::ref does not have a default constructor, by definition.

It is common situation to have a bunch of x::refs getting created as part of an initialization phase, of some kind; however each x::ref's construction is a non-trivial process. Perhaps, for some reason, some of the x::ref's can only be constructed in a different, inner scope, then retrieved outside of their construction scope.

A common design pattern is to initially declare x::ptrs, construct and define each one of them when needed, then permanently save each one as a x::ref. When the entire set of objects get constructed, they get all converted to x::refs. The refcollection.xsl stylesheet generates a framework for this kind of a design pattern. The stylesheet converts an XML header file into a C++ class declaration. The simple XML looks like this:

<collection>
  <name>mycollection</name>
  <ref>
    <type>intval_ref</type>
    <name>a</name>
  </ref>
  <ref>
    <type>constintval_ref</type>
    <name>b</name>
  </ref>
</collection>

This file gets processed by the stylesheet processor, such as xsltproc:

# $datadir/libcxx is where LibCXX installs its stylesheets

xsltproc $datadir/libcxx/refcollection.xsl refcollection.xml >refcollection.H

The stylesheet generates a C++ header declaring two classes that look something like this:

struct mycollectionptr {
	x::ptr_cast<intval_ref> a;
	x::ptr_cast<constintval_ref> b;
};

struct mycollection {
	intval_ref a;
	constintval_ref b;

	mycollection(const mycollectionptr &p)
	: a(p.a),
	  b(p.b)
	{}
};

And the typical usage looks like this:

#include <x/refptr_traits.H>

class intvalObj : virtual public x::obj {

// ...
};

typedef x::ref<intvalObj> intval_ref;

typedef x::const_ref<intvalObj> constintval_ref;

#include "refcollection.H"

// ...

class refcollectionObj : virtual public x::obj, public mycollection {

public:
     using mycollection::mycollection;
};

// ============

mycollectionptr ptrs;

// Long-winded code that ends up initializing ptrs.a and ptrs.b

ptrs.validate_or_throw_exception();

auto newobj=x::ref<refcollectionObj>::create(ptrs);

// So, now, newobj->a and newobj-> are ready for action, and are
// ordinary references.
	  

The basic approach is to declare a structure with nullable pointer references, which get initialized on their own schedule. Afterwards, the collection of x::ptrs gets passed to a constructor of equivalent x::refs, to convert them all to references, going forward. The first structure has a validate_or_throw_exception() that verifies that all x::ptrs were initialized, otherwise an exception gets thrown naming the offending x::ptr. Not using validate_or_throw_exception() results in an unhelpful Null pointer dereference getting thrown from the constructor, without any indication as to who the guilty party is.

A Makefile.am macro assists in creating an appropriate build rule (see the section called “Using GNU make macros with automake for more information on LibCXX's integration with automake and autoconf):

@LIBCXX_AM@

# ...

$(call REFCOLLECTION_GEN,refcollection.H,$(srcdir)/refcollection.xml)

This macro creates a rule to build refcollection.H from the refcollection.xml source using the stylesheet. The structure of the XML is as follows:

  • The top level element is a collection which contains a name and a list of refs.

    The name gives the name of the classes generated by the stylesheet. The stylesheet generates a header file that declares a class whose name is name, containing the refs, and nameptr, containing the x::ptr equivalent of each ref.

  • Each ref creates a member of both name and nameptr classes. ref contains a name that gives the name of the member, with type giving the member's type.

    The specified type must be an x::ref or an x::const_ref.