From daec7542c935411392e494c611e38074eb74dc62 Mon Sep 17 00:00:00 2001 From: Denis Shelomovskij Date: Tue, 6 Nov 2012 12:51:14 +0400 Subject: [PATCH 1/2] Add templated setter to object's `init` state * add `std.conv.setToInitialState` --- std/conv.d | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/std/conv.d b/std/conv.d index 4a194962fb2..8eb642dadcc 100644 --- a/std/conv.d +++ b/std/conv.d @@ -3642,6 +3642,96 @@ unittest assert(i is k); } + +/** Sets the passed object to its `init` state. + +Use this function instead of dealing with tricky $(D typeid(T).init()). + +Note: be careful as it also changes memory marked as $(D const)/$(D immutable). +*/ +package void setToInitialState(T)(ref T t) +{ + alias Unqual!T U; + + // If there is a compiler-generated elaborate assign, `T` isn't nested, and + // either elaborate copy constructor or elaborate destructor is absent + // `t = T.init` has no side effects. It is either an optimization + // opportunity or a compiler bug. + static if(hasElaborateAssign!T || (!isAssignable!T && !isAssignable!U)) + { + import core.stdc.string; + + // `typeid(T)` will also work but will cost a virtual call per each array + // dimension. We will not be here for [static arrays of] classes so + // there is no problems with `TypeInfo_Class.init` field name clash. + if(auto p = typeid(MultidimensionalStaticArrayElementType!U).init().ptr) + foreach(ref el; asFlatStaticArray((*cast(U*) &t))) + memcpy(&el, p, typeof(el).sizeof); + else + memset(cast(void*) &t, 0, T.sizeof); + } + else static if(!isAssignable!T) + { + (*cast(U*) &t) = U.init; + } + else + { + t = T.init; + } +} + +unittest +{ + int i = -1; + setToInitialState(i); + assert(i == 0); + + static assert(!__traits(compiles, setToInitialState(5))); // doesn't accept rvalue + + static bool exited = false; + + static struct S(int def) + { + int i = def; + @disable this(); + this(this) { assert(0); } + ~this() { assert(exited); } + } + + S!0 s0 = void; s0.i = -1; + setToInitialState(s0); + assert(s0.i == 0); + + S!1 s1 = void; s1.i = -1; + setToInitialState(s1); + assert(s1.i == 1); + + S!1[2][1] sArr = void; + foreach(ref el; sArr[0]) + el.i = -1; + setToInitialState(sArr); + assert(sArr == (S!1[2][1]).init); + + exited = true; +} + +unittest // const +{ + static struct Int1 + { int i = 1; } + + static struct S + { const Int1 i; } + + int i = 0; + static assert(S.sizeof == i.sizeof); + setToInitialState(*cast(S*) &i); + assert(i == 1); i = 0; + + setToInitialState(*cast(const S*) &i); + assert(i == 1); i = 0; +} + // Undocumented for the time being void toTextRange(T, W)(T value, W writer) if (isIntegral!T && isOutputRange!(W, char)) From 4d49b24b6034a3b3f75233c5094d7f10141f5265 Mon Sep 17 00:00:00 2001 From: Denis Shelomovskij Date: Tue, 6 Nov 2012 12:55:53 +0400 Subject: [PATCH 2/2] Add templated struct postblit/destructor callers * add `std.conv.callPostblits` * add `std.conv.callDestructors` --- std/conv.d | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/std/conv.d b/std/conv.d index 8eb642dadcc..a570b2b1beb 100644 --- a/std/conv.d +++ b/std/conv.d @@ -3732,6 +3732,148 @@ unittest // const assert(i == 1); i = 0; } + +/** Calls the postblit of the given object, if any. + +Faster and convenient replacement for $(D typeid(T).postblit(&t)). +*/ +package void callPostblits(T)(ref T t) +{ + static if(hasElaborateCopyConstructor!T) + { + foreach(ref el; asFlatStaticArray(t)) + { + foreach(ref field; el.tupleof) + static if(hasElaborateCopyConstructor!(typeof(field))) + callPostblits(field); + + static if(hasMember!(typeof(el), "__postblit")) + el.__postblit(); + } + } +} + +unittest +{ + int i = -1; + callPostblits(i); // no-op for non-elaborate types + + static assert(!__traits(compiles, callPostblits(5))); // doesn't accept rvalue + + static int[] log; + static void checkLog(int[] arr...) + { assert(log == arr); log = null; } + + static bool exited = false; + + static struct S + { + int i; + @disable this(); + this(this) { log ~= i; } + ~this() { assert(exited); } + } + + S s = void; s.i = -1; + callPostblits(s); + checkLog(-1); + + S[3][2][1] sArr = void; + foreach(j, ref el; *cast(S[6]*) sArr.ptr) + el.i = j; + callPostblits(sArr); + checkLog(0, 1, 2, 3, 4, 5); + + static struct S2 + { + S s; + S[2] sArr; + + @disable this(); + this(this) { log ~= -1; } + ~this() { assert(exited); } + } + + S2 s2 = void; + foreach(j, ref el; *cast(S[3]*) &s2) + el.i = j; + callPostblits(s2); + checkLog(0, 1, 2, -1); + + exited = true; +} + + +/** Calls the destructor of the given object, if any. + +Faster and convenient replacement for $(D typeid(T).destroy(&t)). +*/ +package void callDestructors(T)(ref T t) +{ + static if(hasElaborateDestructor!T) + { + foreach_reverse(ref el; asFlatStaticArray(t)) + { + static if(hasMember!(typeof(el), "__dtor")) + el.__dtor(); + + foreach_reverse(ref field; el.tupleof) + static if(hasElaborateDestructor!(typeof(field))) + callDestructors(field); + } + } +} + +unittest +{ + int i = -1; + callDestructors(i); // no-op for non-elaborate types + + static assert(!__traits(compiles, callDestructors(5))); // doesn't accept rvalue + + static int[] log; + static void checkLog(int[] arr...) + { assert(log == arr); log = null; } + + static bool exited = false; + + static struct S + { + int i; + @disable this(); + this(this) { assert(exited); } + ~this() { log ~= i; } + } + + S s = void; s.i = -1; + callDestructors(s); + checkLog(-1); + + S[3][2][1] sArr = void; + foreach(j, ref el; *cast(S[6]*) sArr.ptr) + el.i = j; + callDestructors(sArr); + checkLog(5, 4, 3, 2, 1, 0); + + static struct S2 + { + S s; + S[2] sArr; + + @disable this(); + this(this) { assert(exited); } + ~this() { log ~= -1; } + } + + S2 s2 = void; + foreach(j, ref el; *cast(S[3]*) &s2) + el.i = j; + callDestructors(s2); + checkLog(-1, 2, 1, 0); + + exited = true; +} + // Undocumented for the time being void toTextRange(T, W)(T value, W writer) if (isIntegral!T && isOutputRange!(W, char))