Skip to content

@MartinNowak /smart_ptr.d
Created

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
import std.traits;
import core.memory;
struct Ptr(T)
{
static if (is(T == class))
alias T RefT;
else
alias T* RefT;
this(RefT p, void function(RefT) deleter)
{
_ptr = p;
del = deleter;
}
private this(RefT ptr, ControlBlock *cb)
{
_ptr = ptr;
this.cb = cb;
}
// copying requires explicit usage of .release or .dup
@disable this(this);
@property Ptr dup()
{
ControlBlock* p = void;
if ((p = cb) !is null)
++p._count;
else
cb = p = alloc!ControlBlock(2, del);
return Ptr(_ptr, p);
}
@property Ptr release()
{
import std.algorithm;
return move(this);
}
// rvalue assign
void opAssign(Ptr other)
{
import std.algorithm;
move(other, this);
}
~this()
{
if (auto p = cb)
{
if (!--p._count)
{
p._del(_ptr);
dealloc!ControlBlock(p);
}
}
else if (auto fn = del)
fn(_ptr);
else
assert(this is Ptr.init);
}
static if (is(T == class))
{
@property inout(T) get() inout
{
return _ptr;
}
}
else
{
@property ref inout(T) get() inout
{
return *_ptr;
}
}
alias get this;
@property bool unique() const pure nothrow
{
return count == 1;
}
@property size_t count() const pure nothrow
{
if (auto p = cb)
return p._count;
else
return del is null ? 0 : 1;
}
private:
@property inout(void function(RefT)) del() inout pure nothrow
{
return (cast(size_t)_pcb & 0x1) ?
cast(typeof(return))(cast(size_t)_pcb & ~cast(size_t)0x1) :
null;
}
@property void del(void function(RefT) val) pure nothrow
{
_pcb = cast(void*)(cast(size_t)val | 0x1);
}
@property inout(ControlBlock)* cb() inout pure nothrow
{
return (cast(size_t)_pcb & 0x1) ? null : cast(typeof(return))_pcb;
}
@property void cb(ControlBlock* val) pure nothrow
{
assert(!(cast(size_t)val & 0x1));
_pcb = val;
}
struct ControlBlock
{
size_t _count;
void function(RefT) _del;
}
RefT _ptr;
void* _pcb;
}
/**
*
*/
Ptr!(T) make(T, Args...)(Args args)
{
return Ptr!T(alloc!T(args), &dealloc!T);
}
private:
auto alloc(T, Args...)(auto ref Args args)
{
import core.stdc.stdlib, std.conv;
static if(is(T == class))
enum sz = __traits(classInstanceSize, T);
else
enum sz = T.sizeof;
auto chunk = malloc(sz)[0 .. sz];
static if (hasAliasing!T)
GC.addRange(chunk.ptr, sz);
return emplace!T(chunk, args);
}
void dealloc(T)(T p) if(is(T == class))
{
import core.stdc.stdlib;
if (p !is null)
{
clear(p);
static if (hasAliasing!T)
GC.removeRange(cast(void*)p);
free(cast(void*)p);
}
}
void dealloc(T)(T* p) if(!is(T == class))
{
import core.stdc.stdlib;
if (p !is null)
{
clear(*p);
static if (hasAliasing!T)
GC.removeRange(cast(void*)p);
free(cast(void*)p);
}
}
unittest
{
Ptr!int p1;
assert(!p1.count);
assert(!p1.unique);
Ptr!int p2 = make!int();
assert(p2.count == 1);
assert(p2.unique);
p1 = p2.dup;
assert(p1.get is p2.get);
assert(p1.count == 2);
assert(p2.count == 2);
auto p3 = p2.dup;
}
version (unittest)
{
struct S
{
this(int val=0)
{
_val = val;
++_n;
}
~this()
{
--_n;
}
int _val;
static size_t _n;
}
class C
{
this(int val=0)
{
_val = val;
++_n;
}
~this()
{
--_n;
}
int _val;
static size_t _n;
}
}
unittest
{
import std.typetuple;
static Ptr!T foo(T)(Ptr!T p)
{
assert(p.unique);
return p.release;
}
static Ptr!T bar(T)(Ptr!T p)
{
assert(!p.unique);
return p.dup;
}
foreach(T; TypeTuple!(S, C))
{
{
scope (success) assert(T._n == 0);
auto ptr1 = make!T(0);
assert(ptr1.count == 1);
ptr1 = foo(ptr1.release);
assert(ptr1.count == 1);
auto ptr2 = foo(ptr1.release);
assert(ptr2.unique);
assert(ptr1.count == 0);
}
{
scope (success) assert(T._n == 0);
auto ptr1 = make!T(0);
ptr1 = bar(ptr1.dup);
assert(ptr1.unique);
auto ptr2 = bar(ptr1.dup);
assert(ptr1.get == ptr2.get);
assert(ptr1.count == 2);
assert(ptr2.count == 2);
}
{
scope (success) assert(T._n == 0);
Ptr!T p1, p2;
static assert(!__traits(compiles, p1 = p2));
p1 = make!T(1);
p2 = make!T(2);
assert(p1._val == 1);
assert(p2._val == 2);
import std.algorithm;
swap(p1, p2);
assert(p1._val == 2);
assert(p2._val == 1);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.