Merge pull request #1009 from monarchdodra/initializeAll

improvements to uninitializedFill && initializeAll
This commit is contained in:
Andrei Alexandrescu 2013-01-11 07:55:04 -08:00
commit 6996f434af

View file

@ -1070,6 +1070,9 @@ contain meaningful content. This is of interest for structs that
define copy constructors (for all other types, fill and define copy constructors (for all other types, fill and
uninitializedFill are equivalent). uninitializedFill are equivalent).
uninitializedFill will only operate on ranges that expose references to its
members and have assignable elements.
Example: Example:
---- ----
struct S { ... } struct S { ... }
@ -1079,41 +1082,24 @@ assert(s == [ 42, 42, 42, 42, 42 ]);
---- ----
*/ */
void uninitializedFill(Range, Value)(Range range, Value filler) void uninitializedFill(Range, Value)(Range range, Value filler)
if (isForwardRange!Range && is(typeof(range.front = filler))) if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = filler)))
{ {
alias ElementType!Range T; alias ElementType!Range T;
static if (hasElaborateCopyConstructor!T) static if (hasElaborateAssign!T)
{
// Must construct stuff by the book // Must construct stuff by the book
for (; !range.empty; range.popFront()) for (; !range.empty; range.popFront())
{ emplace(&range.front(), filler);
emplace(&range.front, filler);
}
}
else else
{
// Doesn't matter whether fill is initialized or not // Doesn't matter whether fill is initialized or not
return fill(range, filler); return fill(range, filler);
}
} }
unittest deprecated("Cannot reliably call uninitializedFill on range that does not expose references. Use fill instead.")
void uninitializedFill(Range, Value)(Range range, Value filler)
if (isInputRange!Range && !hasLvalueElements!Range && is(typeof(range.front = filler)))
{ {
debug(std_algorithm) scope(success) static assert(hasElaborateAssign!T, "Cannot execute uninitializedFill a range that does not expose references, and whose objects have an elaborate assign.");
writeln("unittest @", __FILE__, ":", __LINE__, " done."); return fill(range, filler);
int[] a = [ 1, 2, 3 ];
uninitializedFill(a, 6);
assert(a == [ 6, 6, 6 ]);
void fun0()
{
foreach (i; 0 .. 1000)
{
foreach (ref e; a) e = 6;
}
}
void fun1() { foreach (i; 0 .. 1000) fill(a, 6); }
//void fun2() { foreach (i; 0 .. 1000) fill2(a, 6); }
//writeln(benchmark!(fun0, fun1, fun2)(10000));
} }
/** /**
@ -1121,6 +1107,9 @@ Initializes all elements of a range with their $(D .init)
value. Assumes that the range does not currently contain meaningful value. Assumes that the range does not currently contain meaningful
content. content.
initializeAll will operate on ranges that expose references to its
members and have assignable elements, as well as on (mutable) strings.
Example: Example:
---- ----
struct S { ... } struct S { ... }
@ -1130,56 +1119,119 @@ assert(s == [ 0, 0, 0, 0, 0 ]);
---- ----
*/ */
void initializeAll(Range)(Range range) void initializeAll(Range)(Range range)
if (isForwardRange!Range && is(typeof(range.front = range.front))) if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range)
{ {
alias ElementType!Range T; alias ElementType!Range T;
static assert(is(typeof(&(range.front()))) || !hasElaborateAssign!T, static if (hasElaborateAssign!T)
"Cannot initialize a range that does not expose"
" references to its elements");
static if (!isDynamicArray!Range)
{ {
static if (is(typeof(&(range.front())))) //Elaborate opAssign. Must go the memcpy road.
{ //We avoid calling emplace here, because our goal is to initialize to
// Range exposes references //the static state of T.init,
for (; !range.empty; range.popFront()) //So we want to avoid any un-necassarilly CC'ing of T.init
{ auto p = typeid(T).init().ptr;
memcpy(&(range.front()), &T.init, T.sizeof); if (p)
} for ( ; !range.empty ; range.popFront() )
memcpy(&range.front(), p, T.sizeof);
else
static if (isDynamicArray!Range)
memset(range.ptr, 0, range.length * T.sizeof);
else
for ( ; !range.empty ; range.popFront() )
memset(&range.front(), 0, T.sizeof);
} }
else else
{
// Go the slow route
for (; !range.empty; range.popFront())
{
range.front = filler;
}
}
}
else
{
fill(range, T.init); fill(range, T.init);
} }
// ditto
void initializeAll(Range)(Range range)
if (is(Range == char[]) || is(Range == wchar[]))
{
alias ElementEncodingType!Range T;
range[] = T.init;
} }
unittest unittest
{ {
debug(std_algorithm) scope(success) debug(std_algorithm) scope(success)
writeln("unittest @", __FILE__, ":", __LINE__, " done."); writeln("unittest @", __FILE__, ":", __LINE__, " done.");
int[] a = [ 1, 2, 3 ];
uninitializedFill(a, 6); //Test strings:
assert(a == [ 6, 6, 6 ]); //Must work on narrow strings.
initializeAll(a); //Must reject const
assert(a == [ 0, 0, 0 ]); char[3] a = void;
void fun0() a[].initializeAll();
assert(a[] == [char.init, char.init, char.init]);
string s;
assert(!__traits(compiles, s.initializeAll()));
//Note: Cannot call uninitializedFill on narrow strings
enum e {e1, e2}
e[3] b1 = void;
b1[].initializeAll();
assert(b1[] == [e.e1, e.e1, e.e1]);
e[3] b2 = void;
b2[].uninitializedFill(e.e2);
assert(b2[] == [e.e2, e.e2, e.e2]);
static struct S1
{ {
foreach (i; 0 .. 1000) int i;
}
static struct S2
{ {
foreach (ref e; a) e = 6; int i = 1;
}
static struct S3
{
int i;
this(this){};
}
static struct S4
{
int i = 1;
this(this){};
}
static assert (!hasElaborateAssign!S1);
static assert (!hasElaborateAssign!S2);
static assert ( hasElaborateAssign!S3);
static assert ( hasElaborateAssign!S4);
assert (!typeid(S1).init().ptr);
assert ( typeid(S2).init().ptr);
assert (!typeid(S3).init().ptr);
assert ( typeid(S4).init().ptr);
foreach(S; TypeTuple!(S1, S2, S3, S4))
{
//initializeAll
{
//Array
S[3] ss1 = void;
ss1[].initializeAll();
assert(ss1[] == [S.init, S.init, S.init]);
//Not array
S[3] ss2 = void;
auto sf = ss2[].filter!"true"();
sf.initializeAll();
assert(ss2[] == [S.init, S.init, S.init]);
}
//uninitializedFill
{
//Array
S[3] ss1 = void;
ss1[].uninitializedFill(S(2));
assert(ss1[] == [S(2), S(2), S(2)]);
//Not array
S[3] ss2 = void;
auto sf = ss2[].filter!"true"();
sf.uninitializedFill(S(2));
assert(ss2[] == [S(2), S(2), S(2)]);
} }
} }
void fun1() { foreach (i; 0 .. 1000) fill(a, 6); }
//void fun2() { foreach (i; 0 .. 1000) fill2(a, 6); }
//writeln(benchmark!(fun0, fun1, fun2)(10000));
} }
/** /**