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

NameDescription
this (rhs, ) Forward constructor (merge move and copy constructor).
this (nil) Constructs a RcPtr without managed object. Same as RcPtr.init
this (rhs) Constructs a RcPtr which shares ownership of the object managed by rhs.

Properties

NameTypeDescription
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] boolEquivalent to useCount() == 0 (must be RcPtr.WeakType).
get[get] inout(RcPtr.ElementType)Get reference to managed object of ElementType or value if ElementType is reference type (class or interface) or dynamic array.
length[get] size_tReturns 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_tGenerate hash
useCount[get] ControlType.SharedReturns the number of different RcPtr instances
weakCount[get] ControlType.WeakReturns the number of different RcPtr.WeakType instances

Methods

NameDescription
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.WeakType).
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

NameDescription
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);
}