diff --git a/std/typecons.d b/std/typecons.d index 30e958f6b41..90decf58729 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -542,7 +542,7 @@ template Tuple(Specs...) * The types of the `Tuple`'s components. */ alias Types = staticMap!(extractType, fieldSpecs); - + /// unittest { @@ -554,7 +554,7 @@ template Tuple(Specs...) * The names of the `Tuple`'s components. Unnamed fields have empty names. */ alias fieldNames = staticMap!(extractName, fieldSpecs); - + /// unittest { @@ -570,18 +570,18 @@ template Tuple(Specs...) */ Types expand; mixin(injectNamedFields()); - + /// unittest { auto t1 = tuple(1, " hello ", 2.3); assert(t1.toString() == `Tuple!(int, string, double)(1, " hello ", 2.3)`); - - void takeSeveralTypes(int n, string s, bool b) + + void takeSeveralTypes(int n, string s, bool b) { assert(n == 4 && s == "test" && b == false); } - + auto t2 = tuple(4, "test", false); //t.expand acting as a list of values takeSeveralTypes(t2.expand); @@ -617,8 +617,8 @@ template Tuple(Specs...) * Params: * values = A list of values that are either the same * types as those given by the `Types` field - * of this `Tuple`, or can implicitly convert - * to those types. They must be in the same + * of this `Tuple`, or can implicitly convert + * to those types. They must be in the same * order as they appear in `Types`. */ static if (Types.length > 0) @@ -628,7 +628,7 @@ template Tuple(Specs...) field[] = values[]; } } - + /// unittest { @@ -652,7 +652,7 @@ template Tuple(Specs...) field[i] = values[i]; } } - + /// unittest { @@ -675,15 +675,15 @@ template Tuple(Specs...) { field[] = another.field[]; } - + /// unittest { alias IntVec = Tuple!(int, int, int); alias DubVec = Tuple!(double, double, double); - + IntVec iv = tuple(1, 1, 1); - + //Ok, int can implicitly convert to double DubVec dv = iv; //Error: double cannot implicitly convert to int @@ -694,13 +694,13 @@ template Tuple(Specs...) * Comparison for equality. Two `Tuple`s are considered equal * $(B iff) they fulfill the following criteria: * - * $(UL + * $(UL * $(LI Each `Tuple` is the same length.) - * $(LI For each type `T` on the left-hand side and each type - * `U` on the right-hand side, values of type `T` can be + * $(LI For each type `T` on the left-hand side and each type + * `U` on the right-hand side, values of type `T` can be * compared with values of type `U`.) - * $(LI For each value `v1` on the left-hand side and each value - * `v2` on the right-hand side, the expression `v1 == v2` is + * $(LI For each value `v1` on the left-hand side and each value + * `v2` on the right-hand side, the expression `v1 == v2` is * true.)) * * Params: @@ -715,14 +715,14 @@ template Tuple(Specs...) { return field[] == rhs.field[]; } - + /// ditto bool opEquals(R)(R rhs) const if (areCompatibleTuples!(typeof(this), R, "==")) { return field[] == rhs.field[]; } - + /// unittest { @@ -761,7 +761,7 @@ template Tuple(Specs...) } return 0; } - + /// ditto int opCmp(R)(R rhs) const if (areCompatibleTuples!(typeof(this), R, "<")) @@ -775,8 +775,8 @@ template Tuple(Specs...) } return 0; } - - /** + + /** The first `v1` for which `v1 > v2` is true determines the result. This could lead to unexpected behaviour. */ @@ -785,7 +785,7 @@ template Tuple(Specs...) auto tup1 = tuple(1, 1, 1); auto tup2 = tuple(1, 100, 100); assert(tup1 < tup2); - + //Only the first result matters for comparison tup1[0] = 2; assert(tup1 > tup2); @@ -795,7 +795,7 @@ template Tuple(Specs...) * Assignment from another `Tuple`. * * Params: - * rhs = The source `Tuple` to assign from. Each element of the + * rhs = The source `Tuple` to assign from. Each element of the * source `Tuple` must be implicitly assignable to each * respective element of the target `Tuple`. */ @@ -832,8 +832,8 @@ template Tuple(Specs...) * to = A `size_t` designating the ending position (exclusive) of the slice. * * Returns: - * A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original. - * It has the same types and values as the range `[from, to$(RPAREN)` in + * A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original. + * It has the same types and values as the range `[from, to$(RPAREN)` in * the original. */ @property @@ -842,7 +842,7 @@ template Tuple(Specs...) { return *cast(typeof(return)*) &(field[from]); } - + /// unittest { @@ -856,7 +856,7 @@ template Tuple(Specs...) /** Creates a hash of this `Tuple`. - + Returns: A `size_t` representing the hash of this `Tuple`. */ @@ -867,7 +867,7 @@ template Tuple(Specs...) h += typeid(T).getHash(cast(const void*)&field[i]); return h; } - + void toString(DG)(scope DG sink) { enum header = typeof(this).stringof ~ "(", @@ -921,7 +921,7 @@ unittest auto y = point[1]; } -/** +/** `Tuple` members can be named. It is legal to mix named and unnamed members. The method above is still applicable to all fields. */ @@ -950,10 +950,10 @@ unittest /** Create a copy of a `Tuple` with its fields in reverse order. - + Params: t = The `Tuple` to copy. - + Returns: A copy of `t` with its fields in reverse order. */ @@ -1389,17 +1389,17 @@ unittest /** Constructs a $(D Tuple) object instantiated and initialized according to the given arguments. - + Params: Names = A list of strings naming each successive field of the `Tuple`. Each name matches up with the corresponding field given by `Args`. A name does not have to be provided for every field, but as the names must proceed in order, it is not possible to skip one field and name the next after it. - + args = Values to initialize the `Tuple` with. The `Tuple`'s type will be inferred from the types of the values given. - + Returns: A new `Tuple` with its type inferred from the arguments given. */ @@ -1460,10 +1460,10 @@ unittest /** Returns $(D true) if and only if $(D T) is an instance of $(D std.typecons.Tuple). - + Params: T = The type to check. - + Returns: true if `T` is a `Tuple` type, false otherwise. */ @@ -1599,7 +1599,7 @@ unittest } /** - However, $(D Rebindable!(Widget)) does allow reassignment, + However, $(D Rebindable!(Widget)) does allow reassignment, while otherwise behaving exactly like a $(D const Widget). */ unittest @@ -1621,7 +1621,7 @@ inference. Params: obj = A reference to an object or interface, or an array slice to initialize the `Rebindable` with. - + Returns: A newly constructed `Rebindable` initialized with the given reference. */ @@ -1736,7 +1736,7 @@ unittest Similar to $(D Rebindable!(T)) but strips all qualifiers from the reference as opposed to just constness / immutability. Primary intended use case is with shared (having thread-local reference to shared class data) - + Params: T = A class or interface type. */ @@ -1794,13 +1794,13 @@ unittest Order the provided members to minimize size while preserving alignment. Alignment is not always optimal for 80-bit reals, nor for structs declared as align(1). - + Params: - E = A list of the types to be aligned, representing fields + E = A list of the types to be aligned, representing fields of an aggregate such as a `struct` or `class`. - + names = The names of the fields that are to be aligned. - + Returns: A string to be mixed in to an aggregate, such as a `struct` or `class`. */ @@ -1909,13 +1909,13 @@ Returns: { return _isNull; } - + /// unittest { Nullable!int ni; assert(ni.isNull); - + ni = 0; assert(!ni.isNull); } @@ -1928,13 +1928,13 @@ Forces $(D this) to the null state. .destroy(_value); _isNull = true; } - + /// unittest { Nullable!int ni = 0; assert(!ni.isNull); - + ni.nullify(); assert(ni.isNull); } @@ -1956,7 +1956,7 @@ Params: If this `Nullable` wraps a type that already has a null value (such as a pointer), then assigning the null value to this `Nullable` is no different than assigning any other value of - type `T`, and the resulting code will look very strange. It + type `T`, and the resulting code will look very strange. It is strongly recommended that this be avoided by instead using the version of `Nullable` that takes an additional `nullValue` template argument. @@ -1966,7 +1966,7 @@ unittest //Passes Nullable!(int*) npi; assert(npi.isNull); - + //Passes?! npi = null; assert(!npi.isNull); @@ -1985,17 +1985,17 @@ Returns: assert(!isNull, message); return _value; } - + /// unittest { import std.exception: assertThrown, assertNotThrown; - + Nullable!int ni; - //`get` is implicitly called. Will throw + //`get` is implicitly called. Will throw //an AssertError in non-release mode assertThrown!Throwable(ni == 0); - + ni = 0; assertNotThrown!Throwable(ni == 0); } @@ -2016,21 +2016,21 @@ unittest string address; int customerNum; } - + Nullable!CustomerRecord getByName(string name) { //A bunch of hairy stuff - + return Nullable!CustomerRecord.init; } - + auto queryResult = getByName("Doe, John"); if (!queryResult.isNull) { //Process Mr. Doe's customer record auto address = queryResult.address; auto customerNum = queryResult.customerNum; - + //Do some things with this customer's info } else @@ -2320,7 +2320,7 @@ Nullable!T) because it does not need to store an extra $(D bool). Params: T = The wrapped type for which Nullable provides a null value. - + nullValue = The null value which denotes the null state of this `Nullable`. Must be of type `T`. */ @@ -2375,14 +2375,14 @@ Returns: return _value == nullValue; } } - + /// unittest { Nullable!(int, -1) ni; //Initialized to "null" state assert(ni.isNull); - + ni = 0; assert(!ni.isNull); } @@ -2394,20 +2394,20 @@ Forces $(D this) to the null state. { _value = nullValue; } - + /// unittest { Nullable!(int, -1) ni = 0; assert(!ni.isNull); - + ni = -1; assert(ni.isNull); } /** Assigns $(D value) to the internally-held state. If the assignment -succeeds, $(D this) becomes non-null. No null checks are made. Note +succeeds, $(D this) becomes non-null. No null checks are made. Note that the assignment may leave $(D this) in the null state. Params: @@ -2419,13 +2419,13 @@ Params: { _value = value; } - + /** If this `Nullable` wraps a type that already has a null value (such as a pointer), and that null value is not given for - `nullValue`, then assigning the null value to this `Nullable` - is no different than assigning any other value of type `T`, - and the resulting code will look very strange. It is strongly + `nullValue`, then assigning the null value to this `Nullable` + is no different than assigning any other value of type `T`, + and the resulting code will look very strange. It is strongly recommended that this be avoided by using `T`'s "built in" null value for `nullValue`. */ @@ -2435,7 +2435,7 @@ unittest enum nullVal = cast(int*)0xCAFEBABE; Nullable!(int*, nullVal) npi; assert(npi.isNull); - + //Passes?! npi = null; assert(!npi.isNull); @@ -2456,17 +2456,17 @@ Returns: assert(!isNull, message); return _value; } - + /// unittest { import std.exception: assertThrown, assertNotThrown; - + Nullable!(int, -1) ni; - //`get` is implicitly called. Will throw + //`get` is implicitly called. Will throw //an error in non-release mode assertThrown!Throwable(ni == 0); - + ni = 0; assertNotThrown!Throwable(ni == 0); } @@ -2484,14 +2484,14 @@ unittest Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle) { //Find the needle, returning -1 if not found - + return Nullable!(size_t, size_t.max).init; } - + void sendLunchInvite(string name) { } - + //It's safer than C... auto coworkers = ["Jane", "Jim", "Marry", "Fred"]; auto pos = indexOf(coworkers, "Bob"); @@ -2504,7 +2504,7 @@ unittest { //Bob not found; report the error } - + //And there's no overhead static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof); } @@ -2683,13 +2683,13 @@ Params: { _value = value; } - + /// unittest { NullableRef!int nr = new int(42); assert(nr == 42); - + int* n = new int(1); nr.bind(n); assert(nr == 1); @@ -2705,13 +2705,13 @@ Returns: { return _value is null; } - + /// unittest { NullableRef!int nr; assert(nr.isNull); - + int* n = new int(42); nr.bind(n); assert(!nr.isNull && nr == 42); @@ -2724,13 +2724,13 @@ Forces $(D this) to the null state. { _value = null; } - + /// unittest { NullableRef!int nr = new int(42); assert(!nr.isNull); - + nr.nullify(); assert(nr.isNull); } @@ -2751,16 +2751,16 @@ Params: assert(!isNull, message); *_value = value; } - + /// unittest { import std.exception: assertThrown, assertNotThrown; - + NullableRef!int nr; assert(nr.isNull); assertThrown!Throwable(nr = 42); - + nr.bind(new int(0)); assert(!nr.isNull); assertNotThrown!Throwable(nr = 42); @@ -2777,17 +2777,17 @@ This function is also called for the implicit conversion to $(D T). assert(!isNull, message); return *_value; } - + /// unittest { import std.exception: assertThrown, assertNotThrown; - + NullableRef!int nr; - //`get` is implicitly called. Will throw + //`get` is implicitly called. Will throw //an error in non-release mode assertThrown!Throwable(nr == 0); - + nr.bind(new int(0)); assertNotThrown!Throwable(nr == 0); } @@ -3021,8 +3021,8 @@ unittest /** $(D WhiteHole!Base) is a subclass of $(D Base) which automatically implements -all abstract member functions as functions that always fail. These functions -simply throw an $(D Error) and never return. `Whitehole` is useful for +all abstract member functions as functions that always fail. These functions +simply throw an $(D Error) and never return. `Whitehole` is useful for trapping the use of class member functions that haven't been implemented. The name came from @@ -4865,12 +4865,159 @@ unittest assert(rc1 == 5); // No more allocation, add just one extra reference count auto rc2 = rc1; + assert(rc1.refCountedStore.refCount == 2); // Reference semantics rc2 = 42; assert(rc1 == 42); // the pair will be freed when rc1 and rc2 go out of scope } +// recursively destroy a class instance +private void _destroy(T)(T obj) if (is(T == class)) +{ + foreach (C; TypeTuple!(T, BaseClassesTuple!T)) + { + static if (__traits(hasMember, C, "__dtor")) + (cast(C)obj).__dtor(); + } + // leak monitor b/c destruction would be impure, will be collected by the GC +} + +/// RefCounted implementation for classes. +struct RefCounted(T) + if (is(T == class)) +{ + /// $(D RefCounted) storage implementation. + struct RefCountedStore + { + struct Impl + { + size_t _count; + typeof(&._destroy!T) _destroy; // polymorph destructor + ubyte[__traits(classInstanceSize, T)] _payload; + } + Impl* _store; + + /// Returns `true` if the underlying store has been allocated and initialized. + @property nothrow @safe + bool isInitialized() const + { + return _store !is null; + } + + /// Get the underlying reference count. + @property nothrow @safe + size_t refCount() const + { + return _store._count; + } + + private: + + void destroy() + { + import core.memory : GC; + import core.stdc.stdlib : free; + + _store._destroy(cast(T)_store._payload.ptr); + static if (hasIndirections!T) + GC.removeRange(_store._payload.ptr); + free(_store); + _store = null; + } + } + private RefCountedStore _refCounted; + + /// Returns storage implementation struct. + @property nothrow @safe + ref inout(RefCountedStore) refCountedStore() inout + { + return _refCounted; + } + + /// Boolean conversion for RefCounted, true when initialized. + bool opCast(T:bool)() const @safe + { + return _refCounted.isInitialized; + } + + /// explicit constructor for null initialized RefCounted + this(typeof(null)) + { + } + + /// copy constructor for other RefCounted class + this(U)(RefCounted!U u) + if (is(U == class) && is(U : T)) + { + // possibly polymorph conversion + _refCounted._store = cast(RefCountedStore.Impl*)u._refCounted._store; + // incref + if (_refCounted._store !is null) + ++_refCounted._store._count; + } + + /// Postblit constructor to track reference count. + this(this) + { + ++_refCounted._store._count; + } + + /** + Destructor that destroys and frees the payload when the ref count goes to zero. + + Note: This doesn't destroy a possibly attached monitor and won't call dispose events. + */ + ~this() + { + if (_refCounted._store is null || --_refCounted._store._count) + return; + _refCounted.destroy(); + } + + /// assign another ref counted class (can be a derived class) + void opAssign(U)(RefCounted!U u) + if (is(U == class) && is(U : T)) + { + if (_refCounted._store !is null) + { + if (--_refCounted._store._count == 0) + _refCounted.destroy(); + } + // possibly polymorph conversion + _refCounted._store = cast(RefCountedStore.Impl*)u._refCounted._store; + // incref + if (_refCounted._store !is null) + ++_refCounted._store._count; + } + + /// nullify this RefCounted + void opAssign(typeof(null)) + { + if (_refCounted._store !is null) + { + if (--_refCounted._store._count == 0) + _refCounted.destroy(); + } + _refCounted._store = null; + } + + /** + Get the underlying payload. + + Note that getting a class reference is currently unsafe + as there is currently no way to stop it from escaping. (see DIP69) + */ + @property @system + inout(T) refCountedPayload() inout + { + return cast(inout T) _refCounted._store._payload.ptr; + } + + /// Forward to payload + alias refCountedPayload this; +} + unittest { RefCounted!int* p; @@ -4918,6 +5065,13 @@ unittest swap(p1, p2); } +unittest +{ + static class Foo {} + RefCounted!Foo rc = null; + auto rc2 = RefCounted!Foo(null); +} + // 6606 unittest { @@ -4999,17 +5153,171 @@ unittest // file gets properly closed when last reference is dropped } +/** + * Constructs a `RefCounted` instance of type `T` and initializes it with `args`. + * + * Note: Nested classes and structs cannot be constructed at present time, + * as there is no way to transfer the closure's frame pointer into this function. + * + * Params: + * args = The constructor arguments; can be empty + * Returns: + * An initialized `RefCounted` of type `T`. + * See_Also: + * $(WEB http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared, C++'s make_shared) + */ +RefCounted!T refCounted(T, A...)(auto ref A args) + if (__traits(compiles, new T(args))) +{ + import core.memory : GC; + import core.stdc.stdlib : malloc; + import std.conv : emplace; + import core.exception : onOutOfMemoryError; + + RefCounted!T.RefCountedStore rc = void; + rc._store = cast(rc.Impl*)malloc(rc.Impl.sizeof); + if (rc._store is null) + onOutOfMemoryError(); + + static if (hasIndirections!T) + GC.addRange(rc._store._payload.ptr, T.sizeof); + + static if (is(T == class)) + { + emplace!T(rc._store._payload[], args); + rc._store._destroy = &_destroy!T; + } + else + { + emplace!T(&rc._store._payload, args); + } + + rc._store._count = 1; + RefCounted!T res = void; + res._refCounted = rc; + return res; +} + +/// +@nogc unittest +{ + static struct Foo + { + int val; + } + + // A struct instance will be constructed on the heap using malloc/free. + auto rc1 = refCounted!Foo(5); + assert(rc1.val == 5); + // No more allocation, add just one extra reference count + auto rc2 = rc1; + assert(rc1.refCountedStore.refCount == 2); + // Reference semantics + rc2.val = 42; + assert(rc1.val == 42); + // It's not possible to replace the instance + static assert(!__traits(compiles, rc1 = new Foo(12))); + // the instance will be freed when rc1 and rc2 go out of scope +} + +/// RefCounted also works with classes. +@nogc unittest +{ + static class Foo + { + this(int val) @nogc { this.val = val; } + int val; + } + + // A class instance will be constructed on the heap using malloc/free. + auto rc = refCounted!Foo(5); + assert(rc.val == 5); +} + +/// RefCounted classes are polymorph just like their GC allocated cousins. +@nogc nothrow unittest +{ + static class Base + { + ~this() @nogc nothrow {} + string name() @nogc nothrow { return "base"; } + } + + static class Derived : Base + { + override string name() @nogc { return "derived"; } + } + + auto base = refCounted!Base(); + RefCounted!Base derived = refCounted!Derived(); + + assert(base.name == "base"); + assert(derived.name == "derived"); + + base = derived; // assign derived to base class + assert(base.name == "derived"); + assert(base.refCountedStore.refCount == 2); + + // nullify RefCounted + base = null; + assert(!base); // boolean test + assert(derived); + assert(derived.refCountedStore.refCount == 1); +} + +@nogc unittest +{ + static class Foo + { + static size_t ctor, dtor; + @nogc: + this() { ++ctor; } + ~this() { ++dtor; } + } + + assert(Foo.ctor == 0 && Foo.dtor == 0); + { + auto rc = refCounted!Foo(); + assert(Foo.ctor == 1 && Foo.dtor == 0); + auto rc2 = rc; + assert(Foo.ctor == 1 && Foo.dtor == 0); + } + assert(Foo.ctor == 1 && Foo.dtor == 1); + + static class Bar : Foo + { + static size_t ctor, dtor; + @nogc: + this() { ++ctor; } + ~this() { ++dtor; } + } + + Foo.ctor = Foo.dtor = 0; + { + auto rc = refCounted!Bar(); + assert(Foo.ctor == 1 && Foo.dtor == 0); + assert(Bar.ctor == 1 && Bar.dtor == 0); + RefCounted!Foo rc2; + rc2 = rc; + rc = RefCounted!Bar(); + assert(Foo.ctor == 1 && Foo.dtor == 0); + assert(Bar.ctor == 1 && Bar.dtor == 0); + } + assert(Foo.ctor == 1 && Foo.dtor == 1); + assert(Bar.ctor == 1 && Bar.dtor == 1); +} + /** Creates a proxy for the value `a` that will forward all operations - while disabling implicit conversions. The aliased item `a` must be - an $(B lvalue). This is useful for creating a new type from the - "base" type (though this is $(B not) a subtype-supertype - relationship; the new type is not related to the old type in any way, + while disabling implicit conversions. The aliased item `a` must be + an $(B lvalue). This is useful for creating a new type from the + "base" type (though this is $(B not) a subtype-supertype + relationship; the new type is not related to the old type in any way, by design). - + The new type supports all operations that the underlying type does, including all operators such as `+`, `--`, `<`, `[]`, etc. - + Params: a = The value to act as a proxy for all operations. It must be an lvalue. @@ -5248,29 +5556,29 @@ unittest { //Won't work; the literal '1' is //is an rvalue, not an lvalue - //mixin Proxy!1; - + //mixin Proxy!1; + //Okay, n is an lvalue int n; mixin Proxy!n; - + this(int n) { this.n = n; } } - + NewIntType nit = 0; nit++; assert(nit == 1); - - + + struct NewObjectType { Object obj; //Ok, obj is an lvalue mixin Proxy!obj; - + this (Object o) { obj = o; } } - + NewObjectType not = new Object(); assert(__traits(compiles, not.toHash())); } @@ -5278,24 +5586,24 @@ unittest /** There is one exception to the fact that the new type is not related to the old type. $(LINK2 http://dlang.org/function.html#pseudo-member, Pseudo-member) - functions are usable with the new type; they will be forwarded on to the + functions are usable with the new type; they will be forwarded on to the proxied value. */ unittest { import std.math; - + float f = 1.0; assert(!f.isInfinity); - + struct NewFloat { float _; mixin Proxy!_; - + this(float f) { _ = f; } } - + NewFloat nf = 1.0f; assert(!nf.isInfinity); }