Add assignable-, lvalue- and swappable checks
... for ranges. Also adds "put" for the output ranges. Fix #34.
This commit is contained in:
parent
a7c1e642e9
commit
162db622ea
@ -810,6 +810,114 @@ template isRandomAccessRange(R)
|
|||||||
static assert(!isRandomAccessRange!Range4);
|
static assert(!isRandomAccessRange!Range4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts $(D_PARAM e) into the $(D_PARAM range).
|
||||||
|
*
|
||||||
|
* $(D_PSYMBOL R) should be an output range for $(D_PARAM E).
|
||||||
|
*
|
||||||
|
* $(D_PARAM range) is advanced after putting an element into it if all of the
|
||||||
|
* following conditions are met:
|
||||||
|
*
|
||||||
|
* $(OL
|
||||||
|
* $(LI $(D_PSYMBOL R) is an input range)
|
||||||
|
* $(LI $(D_PSYMBOL R) doesn't define a `put`-method)
|
||||||
|
* $(LI $(D_PARAM e) can be assigned to $(D_INLINECODE range.front)
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* R = Target range type.
|
||||||
|
* E = Source element type.
|
||||||
|
* range = Target range.
|
||||||
|
* e = Source element.
|
||||||
|
*
|
||||||
|
* See_Also: $(D_PSYMBOL isOutputRange).
|
||||||
|
*/
|
||||||
|
void put(R, E)(ref R range, auto ref E e)
|
||||||
|
{
|
||||||
|
static if (__traits(hasMember, R, "put")
|
||||||
|
&& is(typeof((R r, E e) => r.put(e))))
|
||||||
|
{
|
||||||
|
range.put(e);
|
||||||
|
}
|
||||||
|
else static if (isInputRange!R
|
||||||
|
&& is(typeof((R r, E e) => r.front = e)))
|
||||||
|
{
|
||||||
|
range.front = e;
|
||||||
|
range.popFront();
|
||||||
|
}
|
||||||
|
else static if (is(typeof((R r, E e) => r(e))))
|
||||||
|
{
|
||||||
|
range(e);
|
||||||
|
}
|
||||||
|
else static if (isInputRange!E)
|
||||||
|
{
|
||||||
|
for (; !e.empty; e.popFront())
|
||||||
|
{
|
||||||
|
put(range, e.front);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static assert(false, R.stringof ~ " is not an output range for "
|
||||||
|
~ E.stringof);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
int[2] actual;
|
||||||
|
auto slice = actual[];
|
||||||
|
|
||||||
|
put(slice, 2);
|
||||||
|
assert(actual == [2, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
static struct Put
|
||||||
|
{
|
||||||
|
int e;
|
||||||
|
|
||||||
|
void put(int e)
|
||||||
|
{
|
||||||
|
this.e = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Put p;
|
||||||
|
put(p, 2);
|
||||||
|
assert(p.e == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
static struct OpCall
|
||||||
|
{
|
||||||
|
int e;
|
||||||
|
|
||||||
|
void opCall(int e)
|
||||||
|
{
|
||||||
|
this.e = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OpCall oc;
|
||||||
|
put(oc, 2);
|
||||||
|
assert(oc.e == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
int[2] actual;
|
||||||
|
int[2] expected = [2, 3];
|
||||||
|
auto slice = actual[];
|
||||||
|
|
||||||
|
put(slice, expected[]);
|
||||||
|
assert(actual == expected);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether $(D_PARAM R) is an output range for the elemens of type
|
* Determines whether $(D_PARAM R) is an output range for the elemens of type
|
||||||
* $(D_PARAM E).
|
* $(D_PARAM E).
|
||||||
@ -855,26 +963,7 @@ template isRandomAccessRange(R)
|
|||||||
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the
|
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the
|
||||||
* elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise.
|
* elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise.
|
||||||
*/
|
*/
|
||||||
template isOutputRange(R, E)
|
enum bool isOutputRange(R, E) = is(typeof((ref R r, ref E e) => put(r, e)));
|
||||||
{
|
|
||||||
private enum bool doPut(E) = is(typeof((R r, E e) => r.put(e)))
|
|
||||||
|| (isInputRange!R
|
|
||||||
&& is(typeof((R r, E e) => r.front = e)))
|
|
||||||
|| is(typeof((R r, E e) => r(e)));
|
|
||||||
|
|
||||||
static if (doPut!E)
|
|
||||||
{
|
|
||||||
enum bool isOutputRange = true;
|
|
||||||
}
|
|
||||||
else static if (isInputRange!E)
|
|
||||||
{
|
|
||||||
enum bool isOutputRange = doPut!(ElementType!E);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
enum bool isOutputRange = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
@ -1360,7 +1449,7 @@ if (isBidirectionalRange!R)
|
|||||||
/**
|
/**
|
||||||
* Moves the front element of an input range.
|
* Moves the front element of an input range.
|
||||||
*
|
*
|
||||||
* The front element is left in a valid but inspecified state.
|
* The front element is left in a valid but unspecified state.
|
||||||
* $(D_PSYMBOL moveFront) doesn't advances the range, so `popFront` should be
|
* $(D_PSYMBOL moveFront) doesn't advances the range, so `popFront` should be
|
||||||
* probably called after this function.
|
* probably called after this function.
|
||||||
*
|
*
|
||||||
@ -1375,7 +1464,7 @@ if (isBidirectionalRange!R)
|
|||||||
ElementType!R moveFront(R)(R range)
|
ElementType!R moveFront(R)(R range)
|
||||||
if (isInputRange!R)
|
if (isInputRange!R)
|
||||||
{
|
{
|
||||||
static if (is(typeof(R.moveFront)))
|
static if (__traits(hasMember, R, "moveFront"))
|
||||||
{
|
{
|
||||||
return range.moveFront();
|
return range.moveFront();
|
||||||
}
|
}
|
||||||
@ -1473,7 +1562,7 @@ if (isInputRange!R)
|
|||||||
/**
|
/**
|
||||||
* Moves the back element of a bidirectional range.
|
* Moves the back element of a bidirectional range.
|
||||||
*
|
*
|
||||||
* The back element is left in a valid but inspecified state.
|
* The back element is left in a valid but unspecified state.
|
||||||
* $(D_PSYMBOL moveBack) doesn't advances the range, so `popBack` should be
|
* $(D_PSYMBOL moveBack) doesn't advances the range, so `popBack` should be
|
||||||
* probably called after this function.
|
* probably called after this function.
|
||||||
*
|
*
|
||||||
@ -1488,7 +1577,7 @@ if (isInputRange!R)
|
|||||||
ElementType!R moveBack(R)(R range)
|
ElementType!R moveBack(R)(R range)
|
||||||
if (isBidirectionalRange!R)
|
if (isBidirectionalRange!R)
|
||||||
{
|
{
|
||||||
static if (is(typeof(R.moveBack)))
|
static if (__traits(hasMember, R, "moveBack"))
|
||||||
{
|
{
|
||||||
return range.moveBack();
|
return range.moveBack();
|
||||||
}
|
}
|
||||||
@ -1614,10 +1703,10 @@ if (isBidirectionalRange!R)
|
|||||||
/**
|
/**
|
||||||
* Moves the element at the position $(D_PARAM n) out of the range.
|
* Moves the element at the position $(D_PARAM n) out of the range.
|
||||||
*
|
*
|
||||||
* The moved element is left in a valid but inspecified state.
|
* The moved element is left in a valid but unspecified state.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* R = Type of the range.
|
* R = Range type.
|
||||||
* range = Random-access range.
|
* range = Random-access range.
|
||||||
* n = Element position.
|
* n = Element position.
|
||||||
*
|
*
|
||||||
@ -1628,7 +1717,7 @@ if (isBidirectionalRange!R)
|
|||||||
ElementType!R moveAt(R)(R range, size_t n)
|
ElementType!R moveAt(R)(R range, size_t n)
|
||||||
if (isRandomAccessRange!R)
|
if (isRandomAccessRange!R)
|
||||||
{
|
{
|
||||||
static if (is(typeof(R.moveAt)))
|
static if (__traits(hasMember, R, "moveAt"))
|
||||||
{
|
{
|
||||||
return range.moveAt(n);
|
return range.moveAt(n);
|
||||||
}
|
}
|
||||||
@ -1749,8 +1838,8 @@ if (isRandomAccessRange!R)
|
|||||||
*
|
*
|
||||||
* Having mobile elements means for an input range to support
|
* Having mobile elements means for an input range to support
|
||||||
* $(D_PSYMBOL moveFront), for a bidirectional range - both,
|
* $(D_PSYMBOL moveFront), for a bidirectional range - both,
|
||||||
* $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveBack), for a random-access -
|
* $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveBack), for a random-access
|
||||||
* $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveAt).
|
* range - $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveAt).
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* R = Range type.
|
* R = Range type.
|
||||||
@ -1854,3 +1943,229 @@ template hasMobileElements(R)
|
|||||||
}
|
}
|
||||||
static assert(hasMobileElements!RandomAccessRange);
|
static assert(hasMobileElements!RandomAccessRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether $(D_PARAM R) provides access to its elements by
|
||||||
|
* reference.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* R = Range type.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if $(D_PARAM R) has lvalue elements,
|
||||||
|
* $(D_KEYWORD false) otherwise.
|
||||||
|
*/
|
||||||
|
template hasLvalueElements(R)
|
||||||
|
{
|
||||||
|
private alias refDg = (ref ElementType!R e) => e;
|
||||||
|
|
||||||
|
static if (isRandomAccessRange!R)
|
||||||
|
{
|
||||||
|
enum bool hasLvalueElements = is(typeof(refDg(R.init.front)))
|
||||||
|
&& is(typeof(refDg(R.init[0])));
|
||||||
|
}
|
||||||
|
else static if (isBidirectionalRange!R)
|
||||||
|
{
|
||||||
|
enum bool hasLvalueElements = is(typeof(refDg(R.init.front)))
|
||||||
|
&& is(typeof(refDg(R.init.back)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
enum bool hasLvalueElements = is(typeof(refDg(R.init.front)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
static struct R1
|
||||||
|
{
|
||||||
|
enum bool empty = false;
|
||||||
|
|
||||||
|
int front() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void popFront() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static assert(!hasLvalueElements!R1);
|
||||||
|
|
||||||
|
static struct R2
|
||||||
|
{
|
||||||
|
int element;
|
||||||
|
enum bool empty = false;
|
||||||
|
|
||||||
|
ref const(int) front() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
void popFront() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ref const(int) opIndex(size_t) const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static assert(hasLvalueElements!R2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the elements of $(D_PARAM R) are assignable.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* R = Range type.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if the elements of $(D_PARAM R) are assignable
|
||||||
|
* $(D_KEYWORD false) otherwise.
|
||||||
|
*/
|
||||||
|
template hasAssignableElements(R)
|
||||||
|
{
|
||||||
|
static if (isRandomAccessRange!R)
|
||||||
|
{
|
||||||
|
enum bool assignable = is(typeof({R.init.front = R.init.front;}))
|
||||||
|
&& is(typeof({R.init[0] = R.init[0];}));
|
||||||
|
}
|
||||||
|
else static if (isBidirectionalRange!R)
|
||||||
|
{
|
||||||
|
enum bool assignable = is(typeof({R.init.front = R.init.front;}))
|
||||||
|
&& is(typeof({R.init.back = R.init.back;}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
enum bool assignable = is(typeof({R.init.front = R.init.front;}));
|
||||||
|
}
|
||||||
|
enum bool hasAssignableElements = assignable;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
static struct R1
|
||||||
|
{
|
||||||
|
int element;
|
||||||
|
enum bool empty = false;
|
||||||
|
|
||||||
|
ref int front() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
alias back = front;
|
||||||
|
|
||||||
|
void popFront() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
}
|
||||||
|
alias popBack = popFront;
|
||||||
|
|
||||||
|
R1 save() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static assert(hasAssignableElements!R1);
|
||||||
|
|
||||||
|
static struct R2
|
||||||
|
{
|
||||||
|
int element;
|
||||||
|
enum bool empty = false;
|
||||||
|
|
||||||
|
ref const(int) front() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
alias back = front;
|
||||||
|
|
||||||
|
void popFront() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
}
|
||||||
|
alias popBack = popFront;
|
||||||
|
|
||||||
|
R2 save() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static assert(!hasAssignableElements!R2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the elements of $(D_PSYMBOL R) can be swapped with
|
||||||
|
* $(D_PSYMBOL swap).
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* R = Range type.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if $(D_PARAM R) has swappable elements,
|
||||||
|
* $(D_KEYWORD false) otherwise.
|
||||||
|
*/
|
||||||
|
template hasSwappableElements(R)
|
||||||
|
{
|
||||||
|
static if (isRandomAccessRange!R)
|
||||||
|
{
|
||||||
|
enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)))
|
||||||
|
&& is(typeof(swap(R.init[0], R.init[0])));
|
||||||
|
}
|
||||||
|
else static if (isBidirectionalRange!R)
|
||||||
|
{
|
||||||
|
enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)))
|
||||||
|
&& is(typeof(swap(R.init.back, R.init.back)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
static struct R1
|
||||||
|
{
|
||||||
|
int element;
|
||||||
|
enum bool empty = false;
|
||||||
|
|
||||||
|
ref int front() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
alias back = front;
|
||||||
|
|
||||||
|
void popFront() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
}
|
||||||
|
alias popBack = popFront;
|
||||||
|
|
||||||
|
R1 save() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static assert(hasSwappableElements!R1);
|
||||||
|
|
||||||
|
static struct R2
|
||||||
|
{
|
||||||
|
int element;
|
||||||
|
enum bool empty = false;
|
||||||
|
|
||||||
|
int front() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
alias back = front;
|
||||||
|
|
||||||
|
void popFront() @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
}
|
||||||
|
alias popBack = popFront;
|
||||||
|
|
||||||
|
R2 save() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static assert(!hasSwappableElements!R2);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user