x::ref or an x::ptr from this
An x::ref or an x::ptr can be constructed from a native object pointer.
This is frequently used by class methods to create pointers or
references to
the this object:
class widgetObj : virtual public x::obj { // ... }; typedef x::ref<widgetObj> widget; // ... void widgetObj::method() { // ... widget myRef(this); // ... }
This allows a class method to create an x::ref or an x::ptr to the object
pointed by this, so that it can be passed along
to other objects or methods, for example. Note, however:
Creating an x::ref or an x::ptr
from this in the object's constructor
abort()s. Consider the following scenario:
create() creates the object. The new
object's reference count does not get incremented from its initial
value of zero, to one, until the object gets fully constructed.
With the initial reference count still zero, one
the constructors makes
an explicit x::ref<classname>(this)
creating a new x::ref. This increments the object's reference
count to one.
This x::ref goes out of scope and gets destroyed, also before
the constructor finishes. This now decrements the reference count
back to zero. When an object's reference count goes to 0,
it gets destroyed. This is now happening before the object
is fully constructed. Even if that, by some miracle
of miracles, doesn't blow up, the remaining constructors continue
to execute, and eventually the original x::ref attempts to
increment what it thinks is the initial reference count, in the
object that's already destroyed.
This is undefined behavior. The same holds true with
creating an x::ref to an object its destructor. The
object's reference count is zero, and it's getting destroyed
no matter what. A new x::ref
(or an x::ptr, doesn't matter) to the object does not prevent that
from happening. The end result is an x::ref or an x::ptr to a destroyed object.
For this reason, attempting to create an x::ref or an x::ptr to
this in its constructor or destructor immediately
abort()s, to aid in debugging.
It's true that it's technically possible to have everything be legal
with a x::ref or an x::ptr from this created in its
constructor, as long as the object succesfully finishes its full
construction. However, LibCXX takes the pessimistic view that
this can't be guaranteed (on the assumption that an exception can
get thrown at any time), and prevents this from happening.
Constructing an x::ref or an x::ptr from this in the
constructor is a bad idea.
Technical note:
it's not really true that the a newly-constructed object starts
with a reference count of 0. Internally, LibCXX sets the
initial reference count to -1, and resets it to 0 just before
create() creates and returns the first
x::ref or an x::ptr, which immediately updates it to 1. The reference
count of -1 gets used to detect an attempt to create an x::ref or an x::ptr
in a constructor (or the destructor).
For this reason, new reference-counted objects must always get
created with create() in order to
properly initialize the new object's reference count. Manually
instantiating the object with new, and
assigning it to an x::ref or an x::ptr, also aborts the program.
constructor() is an ordinary class method,
but if it exists
create()
calls it automatically after constructing a new
object, but before returning the new
x::ref or the new x::ptr.
This is a convenient workaround for not being able to
create an x::ref or an x::ptr in
a real constructor.
A class with a constructor() method must
inherit from
x::with_constructorObj
in addition to x::obj
class buttonObj : x::with_constructorObj, virtual public x::obj { public: buttonObj(int width, int height); void constructor(int width, int height); }; auto b=x::ref<buttonObj>::create(100, 200);
create() forwards its parameters to
the newly-constructed object normally. Afterwards,
create() calls the
constructor() method with the same
parameters that were forwarded to create().
The
constructor() method must be callable with
the same parameters and should return void.
When constructor() gets invoked its object
is fully constructed, for the purpose of the C++ language itself,
and for the purpose of using x::refs and x::ptrs.
The ban on creating x::refs and x::ptrs from
this in constructors do not apply to the
constructor() method itseld. It's just
an ordinary class method.
A constructor() may be viewed as a
“second phase” of constructing an object, and may be
utilized for other reasons besides the ability to create
x::refs and x::ptrs from this. If the class's contract requires
some kind of an initialization method that always gets invoked after
its construction, constructor() is this
method. This becomes a part of the class's contract.
x::with_constructorObj should not be
virtually-inherited.
x::ref or an x::ptrclass buttonObj : virtual public x::obj { // ... void clicked(); }; void buttonObj::clicked() { auto r=x::ref(this); };
A natural deduction guide for an x::ref or an x::ptr (also x::const_ref and
x::const_ptr too) simplifies creating an x::ref or an x::ptr from
this (or from some other native pointer).
Note that this results in the
default base
typedef of
x::ptrref_base, for the constructed
reference or a pointer. However the deduction guide uses the
x::base_type specialization, if one is
available:
class buttonObj : virtual public x::obj { // ... void clicked(); }; template<> struct x::base_type<buttonObj> { typedef buttonBase type; }; void buttonObj::clicked() { auto r=x::ref(this); };
Because this specialization overrides the second template parameter
to an x::ref or an x::ptr, this constructs a
x::ref<buttonObj, buttonBase>.