#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).
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.