#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::ref
s 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::ptr
s, 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::ref
s.
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::ptr
s gets passed to a constructor
of equivalent x::ref
s, to convert them all to references, going
forward.
The first structure has a
validate_or_throw_exception
()
that verifies that all x::ptr
s 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
ref
s.
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 ref
s, and
“name
ptr”, containing
the x::ptr
equivalent of each ref
.
Each ref
creates a member of both
“name
” and
“name
ptr” 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
.