codepad
[
create a new paste
]
login
|
about
Language:
C
C++
D
Haskell
Lua
OCaml
PHP
Perl
Plain Text
Python
Ruby
Scheme
Tcl
module ext.unique; import std.typecons : Proxy; /** Encapsulates unique ownership of a resource (object or struct). Unique controls the lifetime of owned resource; it also provides interface for transferring ownership to/from other Uniques in a way that at any given time only one Unique has ownership. Unique behaves like an object it owns: it accepts method calls and access to fields. But it cannot be temporarily converted to T (e.g. via cast), only permanent conversion that transfers ownership is allowed (via operator <<). Note: in debug builds, attempts to call methods or access fields are checked to detect null pointer dereferencing. If T is a polymorphic type, Unique also behaves polymorphically: - A Unique!D can be assigned to Unique!B if D can be assigned to B - A Unique!B can be cast to Unique!D if B can be cast to D Default-constructed Uniques do not own any resources. Associating a resource with a Unique is performed either with createUnique() function, or by transferring ownership from another Unique. Unique is non-copyable. */ struct Unique(T) if(canBeUnique!T) { static if (is(T==struct)) { alias T* RefT; } else { alias T RefT; } private RefT p = null; private @property inout(RefT) get() inout @safe in { assert(p, "Dereferencing null pointer"); } body { return p; } mixin Proxy!get; /** Constructs a Unique from an rvalue Unique of the same (or convertible) type. Ownership is transferred from other to this. Example: --- class Widget { ... }; class Button : public Widget { ... }; Unique!Widget widget = createUnique!Button(); // Button inherits Widget, conversion ok --- */ this(D : T)(Unique!D other) @safe in { assert(!p); } out { assert(!other && p); } body { p = .transferUnique(other); } /** Constructs a Unique from a null value. This is equivalent to default initialization. */ this(N : typeof(null))(N Null) @safe nothrow in { assert(!p); } out { assert(!p); } body { } /** Destroys owned object. */ ~this() @safe out { assert(!p); } body { .clearUnique(this); } /** Assignment of an rvalue Unique of the same (or convertible) type. Currently owned object is destroyed and ownership is transferred from other to this. Example: --- class Widget { ... }; class Button : public Widget { ... }; auto widget = createUnique!Widget(); assert(widget); ... widget = createUnique!Button(); assert(widget); --- */ ref Unique opAssign(D : T)(Unique!D other) @safe out { assert(!other && p !is null); } body { .clearUnique(this); p = .transferUnique(other); return this; } /** Assigns a null value to this Unique, forcing it to release ownership and destroy currently owned object. */ ref Unique opAssign(N : typeof(null))(N Null) @safe out { assert(!p); } body { .clearUnique(this); return this; } /** Boolean conversion for checking if a Unique currently owns an object. */ Bool opCast(Bool : bool)() inout pure @safe nothrow { return p !is null; } /** Casts a Unique into a Unique of different type, to which T must be convertible. Returns a new Unique that obtains ownership. This is equivalent to simple assignment. If no conversion is required, a Unique is returned as is. Example: --- class Widget {} class Button : Widget {} Unique!Widget widget; auto button = createUnique!Button; widget = cast(Unique!Widget)button; // explicit cast, button is now null, widget owns the object button = createUnique!Button; widget = button; // assignment, button is now null, widget owns the object --- */ B opCast(B : Unique!Base, Base)() @safe nothrow if (is(T : Base)) out { static if(!is(B == typeof(this))) assert(!p); } body { static if (is(B == typeof(this))) return this; else return release!Base(); } /** Casts a Unique into a Unique of different type which must be convertible to T (runtime-checked downcast). Returns a new Unique that obtains ownership. Returned Unique may be null if the original is null, or if cast returns null. If no conversion is required, a Unique is returned as is. Example: --- class Widget {} class Button : Widget {} Unique!Widget widget = createUnique!Button(); // ... auto button = cast(Unique!Button)widget; if (button) { // result of a cast may be null // Use button... } --- */ D opCast(D : Unique!Derived, Derived)() @safe nothrow if (is(Derived : T)) out { static if(!is(D == typeof(this))) assert(!p); } body { static if (is(D == typeof(this))) return this; else return D(cast(Derived).transferUnique(this)); } /** Forces a Unique to release owned object into actual object of type T. Example: --- void consumeWidget(Unique!Widget widget) in { assert(widget); } out { assert(!widget); } body { Widget w; w << widget; // Use w... } --- */ auto opBinaryRight(string op, B = T)(ref B lhs) @safe nothrow if (op == "<<" && is(T : B)) out { assert(!p); } body { T deref(RefT r) @safe nothrow { static if (is(T==struct)) { return r ? *r : T.init; } else { return r; } } lhs = deref(.transferUnique(this)); return lhs; } /** Transfers ownership from this Unique into returned Unique. Call this when another Unique expects to take ownership, i.e. when passing as a parameter into a function. Example: --- Unique!Widget upgradeWidget(Unique!Widget w) { ... } ... auto widget = createUnique!Widget(); // Pass ownership into function, // and then get (possibly different) widget back widget = upgradeWidget(widget.release()); --- Optional template parameter lets you specify a type for the returned Unique. T must be convertible to this type. This helps in cases when explicit conversion is needed. Example: --- class Widget { ... } class Button : Widget { ... } void consumeWidget(Unique!Widget w) { ... } ... auto button = createUnique!Button(); consumeWidget(button.release()); // ERROR, even though there is an appropriate constructor consumeWidget(Unique!Widget(button.release())); // ok, Unique!Widget is constructible from Unique!Button consumeWidget(button.release!Widget()); // ok, conversion --- */ Unique!B release(B = T)() @safe nothrow if (is(T : B)) out { assert(!p); } body { return Unique!B(.transferUnique(this)); } // Rvalue constructor, called by makeUnique() // Strangely, it compiles even though template overloads are present?.. private this(RefT p) @safe nothrow in { assert(p); } out { assert(this.p is p); } body { this.p = p; } // Forbidden operations @disable this(this); @disable void opAssign(ref Unique); @disable this(ref RefT); // TODO: disable new? //@disable new(size_t); } /** Creates a new Unique owning a resource of type T. Params: T = type (class or struct) to instantiate args = arguments to pass to T's constructor */ Unique!T createUnique(T,Args...)(Args args) /*@trusted*/ if (canBeUnique!T) { static if(is(T == class)||is(T == struct)) { return Unique!T(new T(args)); } else { static assert(false, "createUnique is only defined for classes and structs"); } } // // Private implementation // // Transfers an object out of a Unique private auto transferUnique(T)(ref Unique!T u) @safe nothrow out { assert(!u); } body { scope(exit) u.p = null; return u.p; } // Deletes owned object and nullifies a Unique private void clearUnique(T)(ref Unique!T u) @trusted out { assert(!u); } body { if (u.p) { auto p = .transferUnique(u); destroy(p); } } private template canBeUnique(T) { enum canBeUnique = is(T == class) ||is(T == interface) ||is(T == struct); } //================================================================= module tests.unique; import ext.unique; version(unittest) private { struct Point { int x = 0, y = 0; this(int x, int y) @safe { this.x = x; this.y = y; } } interface BaseWidget { void upgrade() @safe; } class Widget : BaseWidget { private string _name; this() @safe { this("unnamed"); } this(string name) @safe { _name = name; } ~this() @safe {} override void upgrade() @safe {} int release() @safe { return 0; } @property string name() @safe inout { return _name; } } class Gadget : Widget { this() @safe { super("Gadget"); } this(string name) @safe { super("Gadget " ~ name); } ~this() @safe {} } // This last one is for promotion checks class Wreck : Gadget {} } unittest { { auto x = createUnique!Point(); // No direct initialization static assert(!__traits(compiles, { Unique!Point x = Point(); })); static assert(!__traits(compiles, { Unique!Point x = Point(320,240); })); static assert(!__traits(compiles, { Unique!Point x = new Point(); })); static assert(!__traits(compiles, { Unique!Point x = new Point(320,240); })); static assert(!__traits(compiles, { Unique!Widget x = new Widget(); })); static assert(!__traits(compiles, { Unique!Widget x = new Widget("button"); })); static assert(!__traits(compiles, { Unique!Widget x = new Gadget(); })); static assert(!__traits(compiles, { Unique!Widget x = new Gadget("ifon"); })); static assert(!__traits(compiles, { Unique!Gadget x = new Widget(); })); static assert(!__traits(compiles, { Unique!Gadget x = new Widget("button"); })); static assert(!__traits(compiles, { Unique!Gadget x = new Gadget(); })); static assert(!__traits(compiles, { Unique!Gadget x = new Gadget("ifon"); })); // Initialization via createUniqe() static assert( __traits(compiles, { Unique!Point x = createUnique!Point(); })); static assert( __traits(compiles, { Unique!Point x = createUnique!Point(320,240); })); static assert( __traits(compiles, { Unique!BaseWidget x = createUnique!Widget(); })); static assert( __traits(compiles, { Unique!BaseWidget x = createUnique!Gadget(); })); static assert( __traits(compiles, { Unique!Widget x = createUnique!Widget(); })); static assert( __traits(compiles, { Unique!Widget x = createUnique!Widget("button"); })); static assert( __traits(compiles, { Unique!Widget x = createUnique!Gadget(); })); static assert( __traits(compiles, { Unique!Widget x = createUnique!Gadget("ifon"); })); // No promotions static assert(!__traits(compiles, { Unique!Gadget x = createUnique!Widget(); })); static assert(!__traits(compiles, { Unique!Gadget x = createUnique!Widget("button"); })); static assert( __traits(compiles, { Unique!Gadget x = createUnique!Gadget(); })); static assert( __traits(compiles, { Unique!Gadget x = createUnique!Gadget("ifon"); })); // Null static assert( __traits(compiles, { Unique!Point x = null; })); static assert( __traits(compiles, { Unique!Widget x = null; })); static assert( __traits(compiles, { Unique!Point x; x = null; })); static assert( __traits(compiles, { Unique!Widget x; x = null; })); // Casts static assert( __traits(compiles, { Unique!Point x; if (x) {} })); static assert( __traits(compiles, { Unique!Widget x; x = cast(Unique!Widget)(createUnique!Gadget()); })); static assert( __traits(compiles, { Unique!Widget x; auto y = cast(Unique!Gadget)(x); })); } { // Type consistency auto x = createUnique!Point(); static assert(is(typeof(x) == Unique!Point)); auto y = createUnique!Point(320,240); static assert(is(typeof(y) == Unique!Point)); auto w = createUnique!Widget(); static assert(is(typeof(w) == Unique!Widget)); auto g = createUnique!Gadget(); static assert(is(typeof(g) == Unique!Gadget)); assert(x); assert(y); assert(w); assert(g); auto ww = createUnique!Widget("button"); static assert(is(typeof(ww) == Unique!Widget)); auto gg = createUnique!Gadget("ifon"); static assert(is(typeof(gg) == Unique!Gadget)); assert(ww); assert(gg); // No lvalue assignments static assert(!__traits(compiles, { x = y; })); static assert(!__traits(compiles, { w = g; })); static assert(!__traits(compiles, { g = w; })); // Rvalue assignments static assert(!__traits(compiles, { w = createUnique!Point(); })); static assert( __traits(compiles, { w = createUnique!Widget(); })); static assert( __traits(compiles, { w = createUnique!Gadget(); })); static assert(!__traits(compiles, { g = createUnique!Widget(); })); static assert( __traits(compiles, { g = createUnique!Gadget(); })); } { // Null Unique!Point up = null; Unique!Widget uw = null; assert(!up); assert(!uw); up = createUnique!Point(); assert(up); uw = createUnique!Widget(); assert(uw); up = null; assert(!up); uw = null; assert(!uw); } { // Transferring ownership with release void consumeWidget(Unique!Widget w) { assert(w); } Unique!Widget upgradeWidget(Unique!Widget w) { assert(w); w.upgrade(); return w.release(); } // We have no implicit casts, and even though an *appropriate ctor* exists, // base type should be specified explicitly :( Unique!Widget downgradeGadget(Unique!Gadget g) { assert(g); return g.release!Widget(); } auto w = createUnique!Widget(); assert(w); auto g = createUnique!Gadget(); assert(g); // Downcast static assert( __traits(compiles, { g.release!Widget(); })); // No promotions static assert(!__traits(compiles, { g.release!Wreck(); })); static assert(!__traits(compiles, { consumeWidget(w); })); static assert( __traits(compiles, { consumeWidget(w.release()); })); static assert(!__traits(compiles, { consumeWidget(g); })); // ditto downgradeGadget() static assert( __traits(compiles, { consumeWidget(g.release!Widget()); })); static assert( __traits(compiles, { w = upgradeWidget(w.release()); })); // ditto downgradeGadget() static assert( __traits(compiles, { w = upgradeWidget(g.release!Widget()); })); consumeWidget(w.release()); assert(!w); w = upgradeWidget(g.release!Widget()); assert(!g); assert(w); w = downgradeGadget(createUnique!Gadget()); assert(w); } { // Proxy collision resolution, just in case auto w = createUnique!Widget(); static assert( __traits(compiles, { w.opDispatch!("release")(); })); auto x = w.opDispatch!("release")(); static assert(is(typeof(x) == int)); } { // Extracting owned object from a Unique static assert(!__traits(compiles, { Point x = createUnique!Point(); })); static assert(!__traits(compiles, { Widget x = createUnique!Widget(); })); static assert(!__traits(compiles, { Widget x = createUnique!Gadget(); })); static assert( __traits(compiles, { Point x; x << createUnique!Point(); })); static assert( __traits(compiles, { Widget x; x << createUnique!Widget(); })); static assert( __traits(compiles, { Widget x; x << createUnique!Gadget(); })); Point p; assert(!p.x); assert(!p.y); auto up = createUnique!Point(320, 240); assert(up); assert(up.x == 320); assert(up.y == 240); p << up; assert(!up); assert(p.x == 320); assert(p.y == 240); p << createUnique!Point(640, 480); assert(p.x == 640); assert(p.y == 480); Widget w = null; auto uw = createUnique!Widget("button"); assert(uw.name == "button"); w << uw; assert(!uw); assert(w); assert(w.name == "button"); w << createUnique!Gadget("shiny"); assert(w); assert(w.name == "Gadget shiny"); assert(cast(Gadget)w); } }
Private
[
?
]
Run code
Submit