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;
foreach (i, T; result.Types)
{
emplaceRef(result[i], seed);
emplaceRef!T(result[i], seed);
}
r.popFront();
return reduce(result, r);
@ -865,7 +865,7 @@ template reduce(fun...) if (fun.length >= 1)
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
for (; !range.empty; range.popFront())
emplaceRef(range.front, filler);
emplaceRef!T(range.front, filler);
}
else
// 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;
foreach (e; r)
{
emplaceRef(result[i], e);
emplaceRef!E(result[i], e);
++i;
}
return cast(E[])result;
@ -110,6 +110,12 @@ unittest
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.
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
{
emplaceRef(tmp[j++], stuff[i]);
emplaceRef!T(tmp[j++], stuff[i]);
}
else
{
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 bigData = bigDataFun();
static if (is(Unqual!T == T))
alias uitem = item;
else
auto ref uitem() @trusted nothrow @property { return cast(Unqual!T)item;}
emplaceRef(bigData[len], uitem);
emplaceRef!T(bigData[len], item);
//We do this at the end, in case of exceptions
_data.arr = bigData;
@ -2484,28 +2485,18 @@ struct Appender(A : T[], T)
auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];}
auto bigData = bigDataFun();
enum mustEmplace = is(typeof(bigData[0].opAssign(cast(Unqual!T)items.front))) ||
!is(typeof(bigData[0] = cast(Unqual!T)items.front));
alias UT = Unqual!T;
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[];
}
else static if (is(Unqual!T == ElementType!Range))
{
foreach (ref it ; bigData[len .. newlen])
{
emplaceRef(it, items.front);
items.popFront();
}
}
else
{
static auto ref getUItem(U)(U item) @trusted {return cast(Unqual!T)item;}
foreach (ref it ; bigData[len .. newlen])
{
emplaceRef(it, getUItem(items.front));
emplaceRef!T(it, items.front);
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
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;})),
format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof));
return emplaceInitializer(chunk);
}
// ditto
package ref T emplaceRef(T, Args...)(ref T chunk, auto ref Args args)
if (!is(T == struct) && Args.length == 1)
static if (!is(T == struct))
ref UT emplaceRef(Arg)(ref UT chunk, auto ref Arg arg)
{
alias Arg = Args[0];
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];})),
static assert(is(typeof({T t = arg;})),
format("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof));
static if (isStaticArray!T)
{
alias UArg = Unqual!Arg;
alias E = typeof(chunk.ptr[0]);
alias E = ElementEncodingType!(typeof(T.init[]));
alias UE = Unqual!E;
enum N = T.length;
static if (is(Arg : T))
{
//Matching static array
static if (!hasElaborateAssign!T && isAssignable!(T, Arg))
static if (!hasElaborateAssign!UT && isAssignable!(UT, Arg))
chunk = arg;
else static if (is(UArg == T))
else static if (is(UArg == UT))
{
memcpy(&chunk, &arg, T.sizeof);
static if (hasElaborateCopyConstructor!T)
typeid(T).postblit(cast(void*)&chunk);
}
else
emplaceRef(chunk, cast(T)arg);
.emplaceRef!T(chunk, cast(T)arg);
}
else static if (is(Arg : E[]))
{
//Matching dynamic array
static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg[])))
static if (!hasElaborateAssign!UT && is(typeof(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");
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);
}
else
emplaceRef(chunk, cast(E[])arg);
.emplaceRef!T(chunk, cast(E[])arg);
}
else static if (is(Arg : E))
{
//Case matching single element to array.
static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg)))
static if (!hasElaborateAssign!UT && is(typeof(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.
//This is as exception safe as what druntime can provide us.
@ -3920,18 +3920,18 @@ if (!is(T == struct) && Args.length == 1)
}
else
//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:
//Types that don't match (int to uint[2])
//Recursion for multidimensions
static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg)))
static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg)))
chunk[] = arg;
else
foreach(i; 0 .. N)
emplaceRef(chunk[i], arg);
.emplaceRef!E(chunk[i], arg);
}
else
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
package ref T emplaceRef(T, Args...)(ref T chunk, auto ref Args args)
if (is(T == struct))
static 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) &&
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
static if (!hasElaborateAssign!T && isAssignable!T)
static if (!hasElaborateAssign!UT && isAssignable!(UT, T))
chunk = args[0];
else
{
@ -3969,7 +3966,7 @@ if (is(T == struct))
}
else
//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))))
{
@ -3991,10 +3988,11 @@ if (is(T == struct))
foreach (i, ref field; chunk.tupleof[0 .. Args.length])
{
alias Field = typeof(field);
static if (is(Field == Unqual!Field))
emplaceRef(field, args[i]);
alias UField = Unqual!Field;
static if (is(Field == UField))
.emplaceRef!Field(field, args[i]);
else
emplaceRef(*cast(Unqual!Field*)&field, args[i]);
.emplaceRef!Field(*cast(Unqual!Field*)&field, args[i]);
}
}
else
@ -4010,6 +4008,7 @@ if (is(T == struct))
return chunk;
}
}
//emplace helper functions
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);})),
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
{
emplaceRef(*chunk);
emplaceRef!T(*chunk);
return chunk;
}
@ -4060,14 +4059,14 @@ as $(D chunk)).
T* emplace(T, Args...)(T* chunk, auto ref Args args)
if (!is(T == struct) && Args.length == 1)
{
emplaceRef(*chunk, args);
emplaceRef!T(*chunk, args);
return chunk;
}
/// ditto
T* emplace(T, Args...)(T* chunk, auto ref Args args)
if (is(T == struct))
{
emplaceRef(*chunk, args);
emplaceRef!T(*chunk, args);
return chunk;
}
@ -4858,6 +4857,32 @@ unittest
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)
{
enforceEx!ConvException(chunk.length >= typeSize,