RcPtr - multiple declarations
Struct RcPtr
Implementation of a ref counted pointer without support for aliasing (smaller size of pointer).
struct RcPtr(_Type, _DestructorType, _ControlType, bool _weakPtr = false)
if (isControlBlock!_ControlType && isDestructorType!_DestructorType);
RcPtr
retains shared ownership of an object through a pointer.
Several ref counted pointer objects may own the same object.
The object is destroyed and its memory deallocated when either of the following happens:
1. the last remaining ref counted pointer owning the object is destroyed.
2. the last remaining ref counted pointer owning the object is assigned another pointer via various methods like opAssign
and store
.
The object is destroyed using destructor of type _Type
.
A RcPtr
can not share ownership of an object while storing a pointer to another object (use SharedPtr
for that).
The stored pointer is the one accessed by get()
, the dereference and the comparison operators.
A RcPtr
may also own no objects, in which case it is called empty.
If template parameter _ControlType
is shared
then all member functions (including copy constructor and copy assignment)
can be called by multiple threads on different instances of RcPtr
without additional synchronization even if these instances are copies and share ownership of the same object.
If multiple threads of execution access the same RcPtr
(shared RcPtr
) then only some methods can be called (load
, store
, exchange
, compareExchange
, useCount
).
Template parameters:
_Type
type of managed object
_DestructorType
function pointer with attributes of destructor, to get attributes of destructor from type use DestructorType!T
. Destructor of type _Type
must be compatible with _DestructorType
_ControlType
represent type of counter, must by of type ControlBlock
. if is shared then ref counting is atomic.
_weakPtr
if true
then RcPtr
represent weak ptr
Constructors
Name | Description |
---|---|
this
(rhs, )
|
Forward constructor (merge move and copy constructor). |
this
(nil)
|
Constructs a RcPtr without managed object. Same as RcPtr
|
this
(rhs)
|
Constructs a RcPtr which shares ownership of the object managed by rhs .
|
Properties
Name | Type | Description |
---|---|---|
element [get]
|
ElementReferenceTypeImpl!(GetElementType!This) | Get pointer to managed object of ElementType or reference if ElementType is reference type (class or interface) or dynamic array.
|
expired [get]
|
bool | Equivalent to useCount() == 0 (must be RcPtr ).
|
get [get]
|
inout(RcPtr | Get reference to managed object of ElementType or value if ElementType is reference type (class or interface) or dynamic array.
|
length [get]
|
size_t | Returns length of dynamic array (isDynamicArray!ElementType == true). |
ptr [get]
|
ElementPointerTypeImpl!(GetElementType!This) | Get pointer to managed object of ElementType or reference if ElementType is reference type (class or interface) or pointer to first dynamic array element.
|
toHash [get]
|
size_t | Generate hash |
useCount [get]
|
ControlType | Returns the number of different RcPtr instances
|
weakCount [get]
|
ControlType | Returns the number of different RcPtr instances
|
Methods
Name | Description |
---|---|
alloc
(a, args)
|
Constructs an object of type ElementType and wraps it in a RcPtr using args as the parameter list for the constructor of ElementType .
|
alloc
(a, n, args)
|
Constructs an object of array type ElementType including its array elements and wraps it in a RcPtr .
|
compareExchangeStrong
(expected, desired)
|
Compares the RcPtr pointers pointed-to by this and expected .
|
compareExchangeWeak
(expected, desired)
|
Same as compareExchangeStrong but may fail spuriously.
|
exchange
()
|
Stores the non shared RcPtr pointer ptr in the shared(RcPtr) pointed to by this and returns the value formerly pointed-to by this, atomically or with mutex.
|
load
()
|
Returns the non shared RcPtr pointer pointed-to by shared this .
|
lock
()
|
Creates a new non weak RcPtr that shares ownership of the managed object (must be RcPtr ).
|
make
(args)
|
Constructs an object of type ElementType and wraps it in a RcPtr using args as the parameter list for the constructor of ElementType .
|
make
(n, args)
|
Constructs an object of array type ElementType including its array elements and wraps it in a RcPtr .
|
opAssign
(nil)
|
Releases the ownership of the managed object, if any. |
opAssign
(desired)
|
Shares ownership of the object managed by rhs .
|
opCast
()
|
Checks if this stores a non-null pointer, i.e. whether this != null .
|
opCast
()
|
Support for quelifier cast. |
opCastImpl
()
|
Cast this to different type To when isRcPtr!To .
|
opCmp
(elm)
|
Operators <, <=, >, >= for RcPtr .
|
opEquals
(nil)
|
Operator == and != . Compare pointers. |
proxySwap
(rhs)
|
Swap this with rhs
|
weak
()
|
Returns weak pointer (must have weak counter). |
Aliases
Name | Description |
---|---|
ControlType
|
Type of control block. |
DestructorType
|
Type of destructor (void function(void*)@attributes ).
|
ElementReferenceType
|
Same as ElementType* or ElementType if is class/interface/slice.
|
ElementType
|
Type of element managed by RcPtr .
|
isLockFree
|
true if shared RcPtr has lock free operations store , load , exchange , compareExchange , otherwise 'false'
|
isWeak
|
true if RcPtr is weak ptr.
|
opUnary
|
Operator *, same as method 'get'. |
SharedType
|
Type of non weak ptr. |
store
|
Stores the non shared RcPtr parameter ptr to this .
|
WeakType
|
Weak pointer |
Alias RcPtr
Alias to RcPtr
with different order of template parameters
alias RcPtr(_Type, _ControlType, _DestructorType)
= RcPtr!(_Type,_DestructorType,_ControlType,false);
Example
static class Foo{
int i;
this(int i)pure nothrow @safe @nogc{
this .i = i;
}
}
static class Bar : Foo{
double d;
this(int i, double d)pure nothrow @safe @nogc{
super(i);
this .d = d;
}
}
static class Zee : Bar{
bool b;
this(int i, double d, bool b)pure nothrow @safe @nogc{
super(i, d);
this .b = b;
}
~this()nothrow @system{
}
}
///simple:
{
RcPtr!long a = RcPtr!long .make(42);
assert(a .useCount == 1);
RcPtr!(const long) b = a;
assert(a .useCount == 2);
RcPtr!long .WeakType w = a .weak; //or WeakRcPtr!long
assert(a .useCount == 2);
assert(a .weakCount == 1);
RcPtr!long c = w .lock;
assert(a .useCount == 3);
assert(a .weakCount == 1);
assert(*c == 42);
assert(c .get == 42);
}
///polymorphism and aliasing:
{
///create RcPtr
RcPtr!Foo foo = RcPtr!Bar .make(42, 3.14);
RcPtr!Zee zee = RcPtr!Zee .make(42, 3.14, false);
///dynamic cast:
RcPtr!Bar bar = dynCast!Bar(foo);
assert(bar != null);
assert(foo .useCount == 2);
///this doesnt work because Foo destructor attributes are more restrictive then Zee's:
//RcPtr!Foo x = zee;
///this does work:
RcPtr!(Foo, DestructorType!(Foo, Zee)) x = zee;
assert(zee .useCount == 2);
}
///multi threading:
{
///create RcPtr with atomic ref counting
RcPtr!(shared Foo) foo = RcPtr!(shared Bar) .make(42, 3.14);
///this doesnt work:
//foo.get.i += 1;
import core .atomic : atomicFetchAdd;
atomicFetchAdd(foo .get .i, 1);
assert(foo .get .i == 43);
///creating `shared(RcPtr)`:
shared RcPtr!(shared Bar) bar = share(dynCast!Bar(foo));
///`shared(RcPtr)` is lock free (except `load` and `useCount`/`weakCount`).
static assert(typeof(bar) .isLockFree == true);
///multi thread operations (`load`, `store`, `exchange` and `compareExchange`):
RcPtr!(shared Bar) bar2 = bar .load();
assert(bar2 != null);
assert(bar2 .useCount == 3);
RcPtr!(shared Bar) bar3 = bar .exchange(null);
assert(bar3 != null);
assert(bar3 .useCount == 3);
}
///dynamic array:
{
import std .algorithm : all, equal;
RcPtr!(long[]) a = RcPtr!(long[]) .make(10, -1);
assert(a .length == 10);
assert(a .get .length == 10);
assert(a .get .all!(x => x == -1));
for(int i = 0; i < a .length; ++i){
a .get[i] = i;
}
assert(a .get[] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
Example
//make RcPtr object
static struct Foo{
int i;
this(int i)pure nothrow @safe @nogc{
this .i = i;
}
}
{
auto foo = RcPtr!Foo .make(42);
auto foo2 = RcPtr!Foo .make!Mallocator(42); //explicit stateless allocator
}
{
auto arr = RcPtr!(long[]) .make(10); //dynamic array with length 10
assert(arr .length == 10);
}
Example
//alloc RcPtr object
import std .experimental .allocator : make, dispose, allocatorObject;
auto allocator = allocatorObject(Mallocator .instance);
{
auto x = RcPtr!(long) .alloc(allocator, 42);
}
{
import btl .internal .traits;
static assert(isMoveConstructableElement!(typeof(allocator)));
auto arr = RcPtr!(long[]) .alloc(allocator, 10); //dynamic array with length 10
assert(arr .length == 10);
}