range.primitive: Support non copyable elements

... in all ranges.
This commit is contained in:
Eugen Wissner 2018-11-19 21:37:58 +01:00
parent 7585bf59e7
commit e67a05138e
7 changed files with 133 additions and 58 deletions

View File

@ -3,13 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Range adapters. * Iteration algorithms.
* *
* A range adapter wraps another range and modifies the way, how the original * These algorithms wrap other ranges and modify the way, how the original
* range is iterated, or the order in which its elements are accessed. * range is iterated, or the order in which its elements are accessed.
* *
* All adapters are lazy algorithms, they request the next element of the * All algorithms in this module are lazy, they request the next element of the
* adapted range on demand. * original range on demand.
* *
* Copyright: Eugene Wissner 2018. * Copyright: Eugene Wissner 2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,

View File

@ -19,6 +19,7 @@ static import tanya.memory.op;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range; import tanya.range;
version (unittest) import tanya.test.stub;
private void deinitialize(bool zero, T)(ref T value) private void deinitialize(bool zero, T)(ref T value)
{ {
@ -554,10 +555,6 @@ if (isInputRange!Range && hasLvalueElements!Range)
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
static struct NonCopyable
{
@disable this(this);
}
NonCopyable[] nonCopyable; NonCopyable[] nonCopyable;
initializeAll(nonCopyable); initializeAll(nonCopyable);
} }

View File

@ -27,6 +27,7 @@ import tanya.range.primitive;
version (unittest) version (unittest)
{ {
import tanya.test.assertion; import tanya.test.assertion;
import tanya.test.stub;
} }
/** /**
@ -257,32 +258,23 @@ out(result; memory.ptr is result)
// Can emplace structs without a constructor // Can emplace structs without a constructor
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
static struct SWithDtor static assert(is(typeof(emplace!WithDtor(null, WithDtor()))));
{ static assert(is(typeof(emplace!WithDtor(null))));
~this() @nogc nothrow pure @safe
{
}
}
static assert(is(typeof(emplace!SWithDtor(null, SWithDtor()))));
static assert(is(typeof(emplace!SWithDtor(null))));
} }
// Doesn't call a destructor on uninitialized elements // Doesn't call a destructor on uninitialized elements
@nogc nothrow pure @system unittest @nogc nothrow pure @system unittest
{ {
static struct WithDtor static struct SWithDtor
{ {
private bool canBeInvoked = false; private bool canBeInvoked = false;
~this() @nogc nothrow pure @safe ~this() @nogc nothrow pure @safe
{ {
if (!this.canBeInvoked) assert(this.canBeInvoked);
{
assert(false);
}
} }
} }
void[WithDtor.sizeof] memory = void; void[SWithDtor.sizeof] memory = void;
auto actual = emplace!WithDtor(memory[], WithDtor(true)); auto actual = emplace!SWithDtor(memory[], SWithDtor(true));
assert(actual.canBeInvoked); assert(actual.canBeInvoked);
} }

View File

@ -30,6 +30,7 @@ import tanya.exception;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.range.primitive; import tanya.range.primitive;
version (unittest) import tanya.test.stub;
private template Payload(T) private template Payload(T)
{ {
@ -611,19 +612,11 @@ do
@nogc @system unittest @nogc @system unittest
{ {
static bool destroyed; size_t destroyed;
static struct F
{ {
~this() @nogc nothrow @safe auto rc = defaultAllocator.refCounted!WithDtor(destroyed);
{
destroyed = true;
}
} }
{ assert(destroyed == 1);
auto rc = defaultAllocator.refCounted!F();
}
assert(destroyed);
} }
/** /**

View File

@ -316,6 +316,26 @@ private template isDynamicArrayRange(R)
} }
} }
private struct Primitive(Candidate, string primitive)
{
auto ref returnType(Candidate candidate)
{
mixin("return candidate." ~ primitive ~ ";");
}
alias ReturnType = .ReturnType!returnType;
static assert(!is(ReturnType == void));
enum uint attributes = functionAttributes!returnType
& FunctionAttribute.ref_;
bool opEquals(That)(That) const
{
return is(ReturnType == That.ReturnType)
&& attributes == That.attributes;
}
}
/** /**
* Determines whether $(D_PARAM R) is an input range. * Determines whether $(D_PARAM R) is an input range.
* *
@ -335,11 +355,11 @@ private template isDynamicArrayRange(R)
*/ */
template isInputRange(R) template isInputRange(R)
{ {
static if (is(ReturnType!((R r) => r.front()) U) static if (is(Primitive!(R, "front()") U)
&& is(ReturnType!((R r) => r.empty) == bool) && is(ReturnType!((R r) => r.empty) == bool)
&& is(typeof(R.popFront()))) && is(typeof(R.popFront())))
{ {
enum bool isInputRange = !is(U == void); enum bool isInputRange = true;
} }
else else
{ {
@ -415,6 +435,28 @@ template isInputRange(R)
static assert(isInputRange!Range4); static assert(isInputRange!Range4);
} }
// Ranges with non-copyable elements can be input ranges
@nogc nothrow pure @safe unittest
{
@WithLvalueElements
static struct R
{
mixin InputRangeStub!NonCopyable;
}
static assert(isInputRange!R);
}
// Ranges with const non-copyable elements can be input ranges
@nogc nothrow pure @safe unittest
{
@WithLvalueElements
static struct R
{
mixin InputRangeStub!(const(NonCopyable));
}
static assert(isInputRange!R);
}
/** /**
* Determines whether $(D_PARAM R) is a forward range. * Determines whether $(D_PARAM R) is a forward range.
* *
@ -521,11 +563,11 @@ template isForwardRange(R)
*/ */
template isBidirectionalRange(R) template isBidirectionalRange(R)
{ {
static if (is(ReturnType!((R r) => r.back()) U) static if (is(Primitive!(R, "back()") U)
&& is(typeof(R.popBack()))) && is(typeof(R.popBack())))
{ {
enum bool isBidirectionalRange = isForwardRange!R enum bool isBidirectionalRange = isForwardRange!R
&& is(U == ReturnType!((R r) => r.front())); && (U() == Primitive!(R, "front()")());
} }
else else
{ {
@ -591,6 +633,17 @@ template isBidirectionalRange(R)
static assert(!isBidirectionalRange!(Range!(int, const int))); static assert(!isBidirectionalRange!(Range!(int, const int)));
} }
// Ranges with non-copyable elements can be bidirectional ranges
@nogc nothrow pure @safe unittest
{
@WithLvalueElements
static struct R
{
mixin BidirectionalRangeStub!NonCopyable;
}
static assert(isBidirectionalRange!R);
}
/** /**
* Determines whether $(D_PARAM R) is a random-access range. * Determines whether $(D_PARAM R) is a random-access range.
* *
@ -616,11 +669,11 @@ template isBidirectionalRange(R)
*/ */
template isRandomAccessRange(R) template isRandomAccessRange(R)
{ {
static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U)) static if (is(Primitive!(R, "opIndex(size_t.init)") U))
{ {
enum bool isRandomAccessRange = isInputRange!R enum bool isRandomAccessRange = isInputRange!R
&& (hasLength!R || isInfinite!R) && (hasLength!R || isInfinite!R)
&& is(U == ReturnType!((R r) => r.front())); && (U() == Primitive!(R, "front()")());
} }
else else
{ {
@ -731,6 +784,17 @@ template isRandomAccessRange(R)
static assert(!isRandomAccessRange!Range4); static assert(!isRandomAccessRange!Range4);
} }
// Ranges with non-copyable elements can be random-access ranges
@nogc nothrow pure @safe unittest
{
@WithLvalueElements @Infinite
static struct R
{
mixin RandomAccessRangeStub!NonCopyable;
}
static assert(isRandomAccessRange!R);
}
/** /**
* Puts $(D_PARAM e) into the $(D_PARAM range). * Puts $(D_PARAM e) into the $(D_PARAM range).
* *
@ -1698,10 +1762,6 @@ template hasLvalueElements(R)
// Works with non-copyable elements // Works with non-copyable elements
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
static struct NonCopyable
{
@disable this(this);
}
static assert(hasLvalueElements!(NonCopyable[])); static assert(hasLvalueElements!(NonCopyable[]));
} }

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Range generators. * Range and generic type generators.
* *
* Copyright: Eugene Wissner 2018. * Copyright: Eugene Wissner 2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
@ -248,6 +248,9 @@ mixin template BidirectionalRangeStub(E = int)
* This mixin includes input range primitives as well, but can be combined with * This mixin includes input range primitives as well, but can be combined with
* $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub).
* *
* Note that a random-access range also requires $(D_PSYMBOL Length) or
* $(D_PARAM Infinite) by definition.
*
* Params: * Params:
* E = Element type. * E = Element type.
*/ */
@ -273,3 +276,37 @@ mixin template RandomAccessRangeStub(E = int)
} }
} }
} }
/**
* Struct with a disabled postblit constructor.
*/
struct NonCopyable
{
@disable this(this);
}
/**
* Struct with an elaborate destructor.
*
* The constructor of $(D_PSYMBOL WithDtor) accepts an additional `counter`
* argument, which is incremented by the destructor. $(D_PSYMBOL WithDtor)
* stores a pointer to the passed variable, so the variable can be
* investigated after the struct isn't available anymore.
*/
struct WithDtor
{
size_t* counter;
this(ref size_t counter) @nogc nothrow pure @trusted
{
this.counter = &counter;
}
~this() @nogc nothrow pure @safe
{
if (this.counter !is null)
{
++*this.counter;
}
}
}

View File

@ -22,6 +22,7 @@ import tanya.format;
import tanya.functional; import tanya.functional;
import tanya.meta.metafunction; import tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.trait;
version (unittest) import tanya.test.stub;
/** /**
* $(D_PSYMBOL Tuple) can store two or more heterogeneous objects. * $(D_PSYMBOL Tuple) can store two or more heterogeneous objects.
@ -454,29 +455,24 @@ struct Option(T)
// Moving // Moving
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
static struct NotCopyable static assert(is(typeof(Option!NonCopyable(NonCopyable()))));
{
@disable this(this);
}
static assert(is(typeof(Option!NotCopyable(NotCopyable()))));
// The value cannot be returned by reference because the default value // The value cannot be returned by reference because the default value
// isn't passed by reference // isn't passed by reference
static assert(!is(typeof(Option!DisabledPostblit().or(NotCopyable())))); static assert(!is(typeof(Option!DisabledPostblit().or(NonCopyable()))));
{ {
NotCopyable notCopyable; NonCopyable notCopyable;
static assert(is(typeof(Option!NotCopyable().or(notCopyable)))); static assert(is(typeof(Option!NonCopyable().or(notCopyable))));
} }
{ {
Option!NotCopyable option; Option!NonCopyable option;
assert(option.isNothing); assert(option.isNothing);
option = NotCopyable(); option = NonCopyable();
assert(!option.isNothing); assert(!option.isNothing);
} }
{ {
Option!NotCopyable option; Option!NonCopyable option;
assert(option.isNothing); assert(option.isNothing);
option = Option!NotCopyable(NotCopyable()); option = Option!NonCopyable(NonCopyable());
assert(!option.isNothing); assert(!option.isNothing);
} }
} }