Merge pull request #2015 from monarchdodra/emplaceQual

Improve emplaceRef for qualified construction
This commit is contained in:
Andrei Alexandrescu 2014-03-26 18:03:39 -07:00
commit cecd745cef
3 changed files with 191 additions and 175 deletions

View file

@ -799,7 +799,7 @@ template reduce(fun...) if (fun.length >= 1)
result = void; result = void;
foreach (i, T; result.Types) foreach (i, T; result.Types)
{ {
emplaceRef(result[i], seed); emplaceRef!T(result[i], seed);
} }
r.popFront(); r.popFront();
return reduce(result, r); return reduce(result, r);
@ -865,7 +865,7 @@ template reduce(fun...) if (fun.length >= 1)
foreach (i, T; result.Types) foreach (i, T; result.Types)
{ {
emplaceRef(result[i], elem); emplaceRef!T(result[i], elem);
} }
} }
} }
@ -1456,7 +1456,7 @@ void uninitializedFill(Range, Value)(Range range, Value filler)
// Must construct stuff by the book // Must construct stuff by the book
for (; !range.empty; range.popFront()) for (; !range.empty; range.popFront())
emplaceRef(range.front, filler); emplaceRef!T(range.front, filler);
} }
else else
// Doesn't matter whether fill is initialized or not // Doesn't matter whether fill is initialized or not

View file

@ -51,7 +51,7 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
size_t i; size_t i;
foreach (e; r) foreach (e; r)
{ {
emplaceRef(result[i], e); emplaceRef!E(result[i], e);
++i; ++i;
} }
return cast(E[])result; return cast(E[])result;
@ -110,6 +110,12 @@ unittest
static assert(bug12315[0].i == 123456789); static assert(bug12315[0].i == 123456789);
} }
unittest
{
static struct S{int* p;}
auto a = array(immutable(S).init.repeat(5));
}
/** /**
Convert a narrow string to an array type that fully supports random access. Convert a narrow string to an array type that fully supports random access.
This is handled as a special case and always returns a $(D dchar[]), This is handled as a special case and always returns a $(D dchar[]),
@ -1059,13 +1065,13 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
{ {
static if (is(E : T)) //ditto static if (is(E : T)) //ditto
{ {
emplaceRef(tmp[j++], stuff[i]); emplaceRef!T(tmp[j++], stuff[i]);
} }
else else
{ {
foreach (v; stuff[i]) foreach (v; stuff[i])
{ {
emplaceRef(tmp[j++], v); emplaceRef!T(tmp[j++], v);
} }
} }
} }
@ -2434,12 +2440,7 @@ struct Appender(A : T[], T)
auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. len + 1];} auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. len + 1];}
auto bigData = bigDataFun(); auto bigData = bigDataFun();
static if (is(Unqual!T == T)) emplaceRef!T(bigData[len], item);
alias uitem = item;
else
auto ref uitem() @trusted nothrow @property { return cast(Unqual!T)item;}
emplaceRef(bigData[len], uitem);
//We do this at the end, in case of exceptions //We do this at the end, in case of exceptions
_data.arr = bigData; _data.arr = bigData;
@ -2484,28 +2485,18 @@ struct Appender(A : T[], T)
auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];} auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];}
auto bigData = bigDataFun(); auto bigData = bigDataFun();
enum mustEmplace = is(typeof(bigData[0].opAssign(cast(Unqual!T)items.front))) || alias UT = Unqual!T;
!is(typeof(bigData[0] = cast(Unqual!T)items.front));
static if (is(typeof(_data.arr[] = items[])) && !mustEmplace) static if (is(typeof(_data.arr[] = items[])) &&
!hasElaborateAssign!(Unqual!T) && isAssignable!(UT, ElementEncodingType!Range))
{ {
//pragma(msg, T.stringof); pragma(msg, Range.stringof);
bigData[len .. newlen] = items[]; bigData[len .. newlen] = items[];
} }
else static if (is(Unqual!T == ElementType!Range))
{
foreach (ref it ; bigData[len .. newlen])
{
emplaceRef(it, items.front);
items.popFront();
}
}
else else
{ {
static auto ref getUItem(U)(U item) @trusted {return cast(Unqual!T)item;}
foreach (ref it ; bigData[len .. newlen]) foreach (ref it ; bigData[len .. newlen])
{ {
emplaceRef(it, getUItem(items.front)); emplaceRef!T(it, items.front);
items.popFront(); items.popFront();
} }
} }

View file

@ -3845,56 +3845,56 @@ emplace, but takes its argument by ref (as opposed to "by pointer").
This makes it easier to use, easier to be safe, and faster in a non-inline This makes it easier to use, easier to be safe, and faster in a non-inline
build. build.
+/
package ref T emplaceRef(T)(ref T chunk)
{
static assert (is(T* : void*),
format("Cannot emplace a %s because it is qualified.", T.stringof));
Furthermore, emplaceRef takes a type paremeter, which specifies the type we
want to build. This helps to build qualified objects on mutable buffer,
without breaking the type system with unsafe casts.
+/
package template emplaceRef(T)
{
alias UT = Unqual!T;
ref UT emplaceRef()(ref UT chunk)
{
static assert (is(typeof({static T i;})), static assert (is(typeof({static T i;})),
format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof));
return emplaceInitializer(chunk); return emplaceInitializer(chunk);
} }
// ditto
package ref T emplaceRef(T, Args...)(ref T chunk, auto ref Args args) static if (!is(T == struct))
if (!is(T == struct) && Args.length == 1) ref UT emplaceRef(Arg)(ref UT chunk, auto ref Arg arg)
{ {
alias Arg = Args[0]; static assert(is(typeof({T t = arg;})),
alias arg = args[0];
static assert (is(T* : void*),
format("Cannot emplace a %s because it is qualified.", T.stringof));
static assert(is(typeof({T t = args[0];})),
format("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof)); format("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof));
static if (isStaticArray!T) static if (isStaticArray!T)
{ {
alias UArg = Unqual!Arg; alias UArg = Unqual!Arg;
alias E = typeof(chunk.ptr[0]); alias E = ElementEncodingType!(typeof(T.init[]));
alias UE = Unqual!E;
enum N = T.length; enum N = T.length;
static if (is(Arg : T)) static if (is(Arg : T))
{ {
//Matching static array //Matching static array
static if (!hasElaborateAssign!T && isAssignable!(T, Arg)) static if (!hasElaborateAssign!UT && isAssignable!(UT, Arg))
chunk = arg; chunk = arg;
else static if (is(UArg == T)) else static if (is(UArg == UT))
{ {
memcpy(&chunk, &arg, T.sizeof); memcpy(&chunk, &arg, T.sizeof);
static if (hasElaborateCopyConstructor!T) static if (hasElaborateCopyConstructor!T)
typeid(T).postblit(cast(void*)&chunk); typeid(T).postblit(cast(void*)&chunk);
} }
else else
emplaceRef(chunk, cast(T)arg); .emplaceRef!T(chunk, cast(T)arg);
} }
else static if (is(Arg : E[])) else static if (is(Arg : E[]))
{ {
//Matching dynamic array //Matching dynamic array
static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg[]))) static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg[])))
chunk[] = arg[]; chunk[] = arg[];
else static if (is(UArg == E[])) else static if (is(Unqual!(ElementEncodingType!Arg) == UE))
{ {
assert(N == chunk.length, "Array length missmatch in emplace"); assert(N == chunk.length, "Array length missmatch in emplace");
memcpy(cast(void*)&chunk, arg.ptr, T.sizeof); memcpy(cast(void*)&chunk, arg.ptr, T.sizeof);
@ -3902,14 +3902,14 @@ if (!is(T == struct) && Args.length == 1)
typeid(T).postblit(cast(void*)&chunk); typeid(T).postblit(cast(void*)&chunk);
} }
else else
emplaceRef(chunk, cast(E[])arg); .emplaceRef!T(chunk, cast(E[])arg);
} }
else static if (is(Arg : E)) else static if (is(Arg : E))
{ {
//Case matching single element to array. //Case matching single element to array.
static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg))) static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg)))
chunk[] = arg; chunk[] = arg;
else static if (is(UArg == E)) else static if (is(UArg == Unqual!E))
{ {
//Note: We copy everything, and then postblit just once. //Note: We copy everything, and then postblit just once.
//This is as exception safe as what druntime can provide us. //This is as exception safe as what druntime can provide us.
@ -3920,18 +3920,18 @@ if (!is(T == struct) && Args.length == 1)
} }
else else
//Alias this. Coerce. //Alias this. Coerce.
emplaceRef(chunk, cast(E)arg); .emplaceRef!T(chunk, cast(E)arg);
} }
else static if (is(typeof(emplaceRef(chunk[0], arg)))) else static if (is(typeof(.emplaceRef!E(chunk[0], arg))))
{ {
//Final case for everything else: //Final case for everything else:
//Types that don't match (int to uint[2]) //Types that don't match (int to uint[2])
//Recursion for multidimensions //Recursion for multidimensions
static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg))) static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg)))
chunk[] = arg; chunk[] = arg;
else else
foreach(i; 0 .. N) foreach(i; 0 .. N)
emplaceRef(chunk[i], arg); .emplaceRef!E(chunk[i], arg);
} }
else else
static assert(0, format("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof)); static assert(0, format("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof));
@ -3945,20 +3945,17 @@ if (!is(T == struct) && Args.length == 1)
} }
} }
// ditto // ditto
package ref T emplaceRef(T, Args...)(ref T chunk, auto ref Args args) static if (is(T == struct))
if (is(T == struct)) ref UT emplaceRef(Args...)(ref UT chunk, auto ref Args args)
{ {
static assert (is(T* : void*),
format("Cannot emplace a %s because it is qualified.", T.stringof));
static if (Args.length == 1 && is(Args[0] : T) && static if (Args.length == 1 && is(Args[0] : T) &&
is (typeof({T t = args[0];})) //Check for legal postblit is (typeof({T t = args[0];})) //Check for legal postblit
) )
{ {
static if (is(T == Unqual!(Args[0]))) static if (is(Unqual!T == Unqual!(Args[0])))
{ {
//Types match exactly: we postblit //Types match exactly: we postblit
static if (!hasElaborateAssign!T && isAssignable!T) static if (!hasElaborateAssign!UT && isAssignable!(UT, T))
chunk = args[0]; chunk = args[0];
else else
{ {
@ -3969,7 +3966,7 @@ if (is(T == struct))
} }
else else
//Alias this. Coerce to type T. //Alias this. Coerce to type T.
emplaceRef(chunk, cast(T)args[0]); .emplaceRef!T(chunk, cast(T)args[0]);
} }
else static if (is(typeof(chunk.__ctor(args)))) else static if (is(typeof(chunk.__ctor(args))))
{ {
@ -3991,10 +3988,11 @@ if (is(T == struct))
foreach (i, ref field; chunk.tupleof[0 .. Args.length]) foreach (i, ref field; chunk.tupleof[0 .. Args.length])
{ {
alias Field = typeof(field); alias Field = typeof(field);
static if (is(Field == Unqual!Field)) alias UField = Unqual!Field;
emplaceRef(field, args[i]); static if (is(Field == UField))
.emplaceRef!Field(field, args[i]);
else else
emplaceRef(*cast(Unqual!Field*)&field, args[i]); .emplaceRef!Field(*cast(Unqual!Field*)&field, args[i]);
} }
} }
else else
@ -4010,6 +4008,7 @@ if (is(T == struct))
return chunk; return chunk;
} }
}
//emplace helper functions //emplace helper functions
private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow
{ {
@ -4027,7 +4026,7 @@ ref T emplaceOpCaller(T, Args...)(ref T chunk, auto ref Args args)
{ {
static assert (is(typeof({T t = T.opCall(args);})), static assert (is(typeof({T t = T.opCall(args);})),
format("%s.opCall does not return adequate data for construction.", T.stringof)); format("%s.opCall does not return adequate data for construction.", T.stringof));
return emplaceRef(chunk, chunk.opCall(args)); return emplaceRef!T(chunk, chunk.opCall(args));
} }
@ -4042,7 +4041,7 @@ as $(D chunk)).
*/ */
T* emplace(T)(T* chunk) @safe nothrow pure T* emplace(T)(T* chunk) @safe nothrow pure
{ {
emplaceRef(*chunk); emplaceRef!T(*chunk);
return chunk; return chunk;
} }
@ -4060,14 +4059,14 @@ as $(D chunk)).
T* emplace(T, Args...)(T* chunk, auto ref Args args) T* emplace(T, Args...)(T* chunk, auto ref Args args)
if (!is(T == struct) && Args.length == 1) if (!is(T == struct) && Args.length == 1)
{ {
emplaceRef(*chunk, args); emplaceRef!T(*chunk, args);
return chunk; return chunk;
} }
/// ditto /// ditto
T* emplace(T, Args...)(T* chunk, auto ref Args args) T* emplace(T, Args...)(T* chunk, auto ref Args args)
if (is(T == struct)) if (is(T == struct))
{ {
emplaceRef(*chunk, args); emplaceRef!T(*chunk, args);
return chunk; return chunk;
} }
@ -4858,6 +4857,32 @@ unittest
emplace(&sss, s); emplace(&sss, s);
} }
unittest //Constness
{
import std.stdio;
int a = void;
emplaceRef!(const int)(a, 5);
immutable i = 5;
const(int)* p = void;
emplaceRef!(const int*)(p, &i);
struct S
{
int* p;
}
alias IS = immutable(S);
S s = void;
emplaceRef!IS(s, IS());
S[2] ss = void;
emplaceRef!(IS[2])(ss, IS());
IS[2] iss = IS.init;
emplaceRef!(IS[2])(ss, iss);
emplaceRef!(IS[2])(ss, iss[]);
}
private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName)
{ {
enforceEx!ConvException(chunk.length >= typeSize, enforceEx!ConvException(chunk.length >= typeSize,