SharedPtr - multiple declarations
Struct SharedPtr
Implementation of a ref counted pointer with support for aliasing.
struct SharedPtr(_Type, _DestructorType, _ControlType, bool _weakPtr = false)
if (isControlBlock!_ControlType && isDestructorType!_DestructorType);
SharedPtr
retains shared ownership of an object through a pointer.
Several SharedPtr
objects may own the same object.
The object is destroyed and its memory deallocated when either of the following happens:
1. the last remaining SharedPtr
owning the object is destroyed.
2. the last remaining SharedPtr
owning the object is assigned another pointer via various methods like opAssign
and store
.
The object is destroyed using delete-expression or a custom deleter that is supplied to SharedPtr
during construction.
A SharedPtr
can share ownership of an object while storing a pointer to another object.
This feature can be used to point to member objects while owning the object they belong to.
The stored pointer is the one accessed by get()
, the dereference and the comparison operators.
The managed pointer is the one passed to the deleter when use count reaches zero.
A SharedPtr
may also own no objects, in which case it is called empty (an empty SharedPtr
may have a non-null stored pointer if the aliasing constructor was used to create it).
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 SharedPtr
without additional synchronization even if these instances are copies and share ownership of the same object.
If multiple threads of execution access the same SharedPtr
(shared SharedPtr
) 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 SharedPtr
represent weak ptr
Constructors
Name | Description |
---|---|
this
(nil)
|
Constructs a SharedPtr without managed object. Same as SharedPtr
|
this
(rhs, )
|
Forward constructor (merge move and copy constructor). |
this
(rhs, element)
|
Constructs a SharedPtr which shares ownership of the object managed by rhs and pointing to element .
|
this
(rhs)
|
Constructs a SharedPtr 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 SharedPtr ).
|
get [get]
|
inout(SharedPtr | 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 SharedPtr instances
|
weakCount [get]
|
ControlType | Returns the number of different SharedPtr instances
|
Methods
Name | Description |
---|---|
alloc
(a, args)
|
Constructs an object of type ElementType and wraps it in a SharedPtr using args as the parameter list for the constructor of ElementType .
|
alloc
(allocator, element, deleter)
|
Constructs a SharedPtr with element as the pointer to the managed object using allocator with state.
|
alloc
(a, n, args)
|
Constructs an object of array type ElementType including its array elements and wraps it in a SharedPtr .
|
compareExchange
(expected, desired)
|
Compares the SharedPtr pointers pointed-to by this and expected .
|
exchange
()
|
Stores the non shared SharedPtr pointer ptr in the shared(SharedPtr) pointed to by this and returns the value formerly pointed-to by this, atomically or with mutex.
|
load
()
|
Returns the non shared SharedPtr pointer pointed-to by shared this .
|
lock
()
|
Creates a new non weak SharedPtr that shares ownership of the managed object (must be SharedPtr ).
|
make
(args)
|
Constructs an object of type ElementType and wraps it in a SharedPtr using args as the parameter list for the constructor of ElementType .
|
make
(element, deleter)
|
Constructs a SharedPtr with element as the pointer to the managed object.
|
make
(n, args)
|
Constructs an object of array type ElementType including its array elements and wraps it in a SharedPtr .
|
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 isSharedPtr!To .
|
opCmp
(elm)
|
Operators <, <=, >, >= for SharedPtr .
|
opEquals
(nil)
|
Operator == and != . Compare pointers. |
proxySwap
(rhs)
|
Swap this with rhs
|
weak
()
|
Returns weak pointer (must have weak counter). |
Aliases
Name | Description |
---|---|
compareExchangeStrong
|
Same as compareExchange .
|
compareExchangeWeak
|
Same as compareExchange .
|
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 SharedPtr .
|
isLockFree
|
true if shared SharedPtr has lock free operations store , load , exchange , compareExchange , otherwise 'false'
|
isWeak
|
true if SharedPtr is weak ptr.
|
opUnary
|
Operator *, same as method 'get'. |
SharedType
|
Type of non weak ptr. |
store
|
Stores the non shared SharedPtr parameter ptr to this .
|
WeakType
|
Weak pointer |
Alias SharedPtr
Alias to SharedPtr
with different order of template parameters
alias SharedPtr(_Type, _ControlType, _DestructorType, bool _weakPtr = false)
= SharedPtr!(_Type,_DestructorType,_ControlType,_weakPtr);
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:
{
SharedPtr!long a = SharedPtr!long .make(42);
assert(a .useCount == 1);
SharedPtr!(const long) b = a;
assert(a .useCount == 2);
SharedPtr!long .WeakType w = a .weak; //or WeakPtr!long
assert(a .useCount == 2);
assert(a .weakCount == 1);
SharedPtr!long c = w .lock;
assert(a .useCount == 3);
assert(a .weakCount == 1);
assert(*c == 42);
assert(c .get == 42);
}
///polymorphism and aliasing:
{
///create SharedPtr
SharedPtr!Foo foo = SharedPtr!Bar .make(42, 3.14);
SharedPtr!Zee zee = SharedPtr!Zee .make(42, 3.14, false);
///dynamic cast:
SharedPtr!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:
//SharedPtr!Foo x = zee;
///this does work:
SharedPtr!(Foo, DestructorType!(Foo, Zee)) x = zee;
assert(zee .useCount == 2);
///aliasing (shared ptr `d` share ref counting with `bar`):
SharedPtr!double d = SharedPtr!double(bar, &bar .get .d);
assert(d != null);
assert(*d == 3.14);
assert(foo .useCount == 3);
}
///multi threading:
{
///create SharedPtr with atomic ref counting
SharedPtr!(shared Foo) foo = SharedPtr!(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(SharedPtr)`:
shared SharedPtr!(shared Bar) bar = share(dynCast!Bar(foo));
///`shared(SharedPtr)` is not lock free but `RcPtr` is lock free.
static assert(typeof(bar) .isLockFree == false);
///multi thread operations (`load`, `store`, `exchange` and `compareExchange`):
SharedPtr!(shared Bar) bar2 = bar .load();
assert(bar2 != null);
assert(bar2 .useCount == 3);
SharedPtr!(shared Bar) bar3 = bar .exchange(null);
assert(bar3 != null);
assert(bar3 .useCount == 3);
}
///dynamic array:
{
import std .algorithm : all, equal;
SharedPtr!(long[]) a = SharedPtr!(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]);
///aliasing:
SharedPtr!long a6 = SharedPtr!long(a, &a .get[6]);
assert(*a6 == a .get[6]);
}
Example
//make SharedPtr object
static struct Foo{
int i;
this(int i)pure nothrow @safe @nogc{
this .i = i;
}
}
{
auto foo = SharedPtr!Foo .make(42);
auto foo2 = SharedPtr!Foo .make!Mallocator(42); //explicit stateless allocator
}
{
import std .experimental .allocator : make, dispose;
static void deleter(long* x)pure nothrow @trusted @nogc{
Mallocator .instance .dispose(x);
}
long* element = Mallocator .instance .make!long;
auto x = SharedPtr!long .make(element, &deleter);
}
{
auto arr = SharedPtr!(long[]) .make(10); //dynamic array with length 10
assert(arr .length == 10);
}
Example
//alloc SharedPtr object
import std .experimental .allocator : make, dispose, allocatorObject;
auto allocator = allocatorObject(Mallocator .instance);
{
auto x = SharedPtr!long .alloc(allocator, 42);
}
{
static void deleter(long* x)pure nothrow @trusted @nogc{
Mallocator .instance .dispose(x);
}
long* element = Mallocator .instance .make!long;
auto x = SharedPtr!long .alloc(allocator, element, &deleter);
}
{
auto arr = SharedPtr!(long[]) .alloc(allocator, 10); //dynamic array with length 10
assert(arr .length == 10);
}