diff --git a/std/typecons.d b/std/typecons.d index 5169783b4..30e958f6b 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -50,11 +50,16 @@ import std.typetuple; // : TypeTuple, allSatisfy; debug(Unique) import std.stdio; /** -Encapsulates unique ownership of a resource. Resource of type $(D T) is -deleted at the end of the scope, unless it is transferred. The -transfer can be explicit, by calling $(D release), or implicit, when -returning Unique from a function. The resource can be a polymorphic -class object, in which case Unique behaves polymorphically too. +Encapsulates unique ownership of a resource. + +Like C++'s $(LINK2 http://en.cppreference.com/w/cpp/memory/unique_ptr, std::unique_ptr), +a $(D Unique) maintains sole ownership of a given resource of type $(D T) until +ownership is transferred or the $(D Unique) falls out of scope. +Such a transfer can be explicit, using +$(LINK2 http://dlang.org/phobos/std_algorithm_mutation.html#.move, $(D std.algorithm.move)), +or implicit, when returning Unique from a function that created it. +The resource can be a polymorphic class object, +in which case Unique behaves polymorphically too. */ struct Unique(T) { @@ -64,67 +69,18 @@ static if (is(T:Object)) else alias RefT = T*; -public: - // Deferred in case we get some language support for checking uniqueness. - version(None) - /** - Allows safe construction of $(D Unique). It creates the resource and - guarantees unique ownership of it (unless $(D T) publishes aliases of - $(D this)). - Note: Nested structs/classes cannot be created. - Params: - args = Arguments to pass to $(D T)'s constructor. - --- - static class C {} - auto u = Unique!(C).create(); - --- - */ - static Unique!T create(A...)(auto ref A args) - if (__traits(compiles, new T(args))) - { - debug(Unique) writeln("Unique.create for ", T.stringof); - Unique!T u; - u._p = new T(args); - return u; - } - - /** - Constructor that takes an rvalue. - It will ensure uniqueness, as long as the rvalue - isn't just a view on an lvalue (e.g., a cast). - Typical usage: - ---- - Unique!Foo f = new Foo; - ---- - */ - this(RefT p) - { - debug(Unique) writeln("Unique constructor with rvalue"); - _p = p; - } - /** - Constructor that takes an lvalue. It nulls its source. - The nulling will ensure uniqueness as long as there - are no previous aliases to the source. - */ - this(ref RefT p) - { - _p = p; - debug(Unique) writeln("Unique constructor nulling source"); - p = null; - assert(p is null); - } /** Constructor that takes a $(D Unique) of a type that is convertible to our type. Typically used to transfer a $(D Unique) rvalue of derived type to a $(D Unique) of base type. + Example: --- - class C : Object {} + class C : Object { } - Unique!C uc = new C; - Unique!Object uo = uc.release; + Unique!C uc = unique!C(); + Unique!Object uo = move(uc); --- */ this(U)(Unique!U u) @@ -146,39 +102,185 @@ public: u._p = null; } + /// Destroying a $(D Unique) frees the underlying resource. ~this() { + import core.stdc.stdlib : free; + debug(Unique) writeln("Unique destructor of ", (_p is null)? null: _p); - if (_p !is null) delete _p; - _p = null; + if (_p !is null) + { + destroy(_p); + + static if (hasIndirections!T) + { + import core.memory : GC; + GC.removeRange(cast(void*)_p); + } + + free(cast(void*)_p); + _p = null; + } } - /** Returns whether the resource exists. */ - @property bool isEmpty() const - { - return _p is null; - } - /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents. */ + + /// Transfer ownership to a $(D Unique) rvalue. + deprecated("Please use std.algorithm.move to transfer ownership.") Unique release() { + import std.algorithm : move; + debug(Unique) writeln("Release"); - auto u = Unique(_p); + Unique u = move(this); assert(_p is null); debug(Unique) writeln("return from Release"); return u; } - /** Forwards member access to contents. */ - RefT opDot() { return _p; } /** - Postblit operator is undefined to prevent the cloning of $(D Unique) objects. + Returns a reference to the underlying $(D RefT) for use by non-owning code. + + The holder of a $(D Unique!T) is the $(I owner) of that $(D T). + For code that does not own the resource (and therefore does not affect + its life cycle), pass a plain old reference. */ + ref T get()() return @safe + if (!is(T == class)) + { + import std.exception : enforce; + enforce(!empty, "You cannot get a struct reference from an empty Unique"); + return *_p; + } + + /** + Returns a the underlying $(D T) for use by non-owning code. + + Note that getting a class reference is currently unsafe + as there is currently no way to stop it from escaping. (see DIP69) + */ + T get()() @system + if (is(T == class)) + { + return _p; + } + + /// Returns true if the $(D Unique) currently owns an underlying $(D T) + @property bool empty() const + { + return _p is null; + } + + /// Allows the $(D Unique) to cast to a boolean value matching + /// that of $(D Unique.empty) + bool opCast(T : bool)() const { return !empty; } + + /// Forwards the underlying $(D RefT) + alias get this; + + /// Postblit operator is undefined to prevent the cloning of $(D Unique) objects. @disable this(this); private: RefT _p; } +unittest +{ + // Ditto... + import std.algorithm; + + static class C : Object { } + + Unique!C uc = unique!C(); + Unique!Object uo = move(uc); +} + + +/** +Allows safe construction of $(D Unique). It creates the resource and +guarantees unique ownership of it (unless $(D T) publishes aliases of +$(D this)). + +Note: Nested classes and structs cannot be created at present time, + as there is no way to transfer the closure's frame pointer + into this function. + +Params: + args = Arguments to pass to $(D T)'s constructor. + +*/ +Unique!T unique(T, A...)(auto ref A args) + if (__traits(compiles, new T(args))) +{ + debug(Unique) writeln("Unique.create for ", T.stringof); + + import core.memory : GC; + import core.stdc.stdlib : malloc; + import std.conv : emplace; + import core.exception : onOutOfMemoryError; + + debug(Unique) writeln("Unique.create for ", T.stringof); + Unique!T u; + + // TODO: May need to fix alignment? + // Does emplace still need to mess with alignment if + // the memory is coming from malloc, or does malloc handle that? + + static if (is(T == class)) + immutable size_t allocSize = __traits(classInstanceSize, T); + else + immutable size_t allocSize = T.sizeof; + + void* rawMemory = malloc(allocSize); + if (!rawMemory) + onOutOfMemoryError(); + + static if (is(T == class)) { + u._p = emplace!T(rawMemory[0 .. allocSize], args); + } + else { + u._p = cast(T*)rawMemory; + emplace!T(u._p, args); + } + + static if (hasIndirections!T) + GC.addRange(rawMemory, allocSize); + + return u; +} + /// +unittest +{ + struct S { } + auto u = unique!S(); + assert(!u.empty()); +} + +unittest +{ + // Some real simple stuff + static struct S + { + int i; + this(int i) { this.i = i; } + } + + // Some quick tests around alias this + auto u = unique!S(42); + assert(u.i == 42); + assert(!u.empty); + u.destroy(); + assert(u.empty); + assert(!u); // Since null pointers coerce to false + + auto i = unique!int(25); + assert(i.get() == 25); + assert(i == 25); + + // opAssign still kicks in, preventing this from compiling: + // i = null; +} + unittest { static struct S @@ -186,88 +288,95 @@ unittest int i; this(int i){this.i = i;} } + + // Test implicit return from a function Unique!S produce() { // Construct a unique instance of S on the heap - Unique!S ut = new S(5); + Unique!S ut = unique!S(5); // Implicit transfer of ownership return ut; } + // Borrow a unique resource by ref + // Note that references to Unique should not be passed around to + // code that does not play a role in the Unique's life cycle. + // (This is what .get() is for) void increment(ref Unique!S ur) { ur.i++; } + + // See above + void correctIncrement(ref S r) + { + r.i++; + } + void consume(Unique!S u2) { - assert(u2.i == 6); + assert(u2.i == 8); // Resource automatically deleted here } + Unique!S u1; - assert(u1.isEmpty); + assert(!u1); u1 = produce(); increment(u1); assert(u1.i == 6); - //consume(u1); // Error: u1 is not copyable + correctIncrement(u1.get()); + // yay alias this + correctIncrement(u1); + assert(u1.i == 8); + + // consume(u1); // Error: u1 is not copyable + // Transfer ownership of the resource - consume(u1.release); - assert(u1.isEmpty); + import std.algorithm : move; + consume(move(u1)); + assert(!u1); } unittest { - // test conversion to base ref - int deleted = 0; - class C - { - ~this(){deleted++;} - } - // constructor conversion - Unique!Object u = Unique!C(new C); - static assert(!__traits(compiles, {u = new C;})); - assert(!u.isEmpty); - destroy(u); - assert(deleted == 1); + // FIXME: Isn't this a bit redundant? + // I believe all of these bases are covered in the tests above. - Unique!C uc = new C; - static assert(!__traits(compiles, {Unique!Object uo = uc;})); - Unique!Object uo = new C; - // opAssign conversion, deleting uo resource first - uo = uc.release; - assert(uc.isEmpty); - assert(!uo.isEmpty); - assert(deleted == 2); -} - -unittest -{ debug(Unique) writeln("Unique class"); - class Bar + static class Bar { ~this() { debug(Unique) writeln(" Bar destructor"); } int val() const { return 4; } } + alias UBar = Unique!(Bar); + UBar g(UBar u) { + import std.algorithm : move; debug(Unique) writeln("inside g"); - return u.release; + return move(u); } - auto ub = UBar(new Bar); - assert(!ub.isEmpty); + + auto ub = unique!Bar(); + assert(ub); assert(ub.val == 4); - static assert(!__traits(compiles, {auto ub3 = g(ub);})); + + import std.algorithm : move; debug(Unique) writeln("Calling g"); - auto ub2 = g(ub.release); + auto ub2 = g(move(ub)); debug(Unique) writeln("Returned from g"); - assert(ub.isEmpty); - assert(!ub2.isEmpty); + assert(!ub); + assert(ub2); } unittest { + // Same as above, but for a struct + import std.algorithm : move; + debug(Unique) writeln("Unique struct"); - struct Foo + static struct Foo { ~this() { debug(Unique) writeln(" Foo destructor"); } int val() const { return 3; } @@ -277,18 +386,17 @@ unittest UFoo f(UFoo u) { debug(Unique) writeln("inside f"); - return u.release; + return move(u); } - auto uf = UFoo(new Foo); - assert(!uf.isEmpty); + auto uf = unique!Foo(); + assert(uf); assert(uf.val == 3); - static assert(!__traits(compiles, {auto uf3 = f(uf);})); debug(Unique) writeln("Unique struct: calling f"); - auto uf2 = f(uf.release); + auto uf2 = f(move(uf)); debug(Unique) writeln("Unique struct: returned from f"); - assert(uf.isEmpty); - assert(!uf2.isEmpty); + assert(!uf); + assert(uf2); }