diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index a66e980..9ad8cdd 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -23,7 +23,7 @@ module tanya.algorithm.iteration; import tanya.algorithm.comparison; import tanya.algorithm.mutation; import tanya.range; -version (unittest) import tanya.test.range; +version (unittest) import tanya.test.stub; private struct Take(R, bool exactly) { @@ -322,10 +322,9 @@ if (isInputRange!R) // length is unknown when taking from a range without length @nogc nothrow pure @safe unittest { - @Empty static struct R { - mixin InputRange; + mixin InputRangeStub; } auto actual = take(R(), 100); diff --git a/source/tanya/meta/trait.d b/source/tanya/meta/trait.d index 5ecb234..69b97b9 100644 --- a/source/tanya/meta/trait.d +++ b/source/tanya/meta/trait.d @@ -2856,6 +2856,46 @@ template hasUDA(alias symbol, alias attr) static assert(!hasUDA!(a, Attr2)); } +/** + * If $(D_PARAM T) is a type, constructs its default value, otherwise + * $(D_PSYMBOL evalUDA) aliases itself to $(D_PARAM T). + * + * This template is useful when working with UDAs with default parameters, + * i.e. if an attribute can be given as `@Attr` or `@Attr("param")`, + * $(D_PSYMBOL evalUDA) makes `@Attr()` from `@Attr`, but returns + * `@Attr("param")` as is. + * + * $(D_PARAM T) (or its type if it isn't a type already) should have a default + * constructor. + * + * Params: + * T = User Defined Attribute. + */ +alias evalUDA(alias T) = T; + +/// ditto +alias evalUDA(T) = Alias!(T()); + +/// +@nogc nothrow pure @safe unittest +{ + static struct Length + { + size_t length = 8; + } + @Length @Length(0) int i; + alias uda = AliasSeq!(__traits(getAttributes, i)); + + alias attr1 = evalUDA!(uda[0]); + alias attr2 = evalUDA!(uda[1]); + + static assert(is(typeof(attr1) == Length)); + static assert(is(typeof(attr2) == Length)); + + static assert(attr1.length == 8); + static assert(attr2.length == 0); +} + /** * Tests whether $(D_PARAM T) is an inner class, i.e. a class nested inside * another class. diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index 934ff31..099208a 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -20,6 +20,19 @@ import tanya.meta.trait; import tanya.meta.transform; import tanya.range.array; +version (unittest) +{ + import tanya.test.stub; + + private struct AssertPostblit + { + this(this) @nogc nothrow pure @safe + { + assert(false); + } + } +} + /** * Returns the element type of the range $(D_PARAM R). * @@ -73,10 +86,7 @@ template ElementType(R) * * See_Also: $(D_PSYMBOL isInfinite). */ -template hasLength(R) -{ - enum bool hasLength = is(ReturnType!((R r) => r.length) == size_t); -} +enum bool hasLength(R) = is(ReturnType!((R r) => r.length) == size_t); /// @nogc nothrow pure @safe unittest @@ -294,34 +304,6 @@ template hasSlicing(R) static assert(hasSlicing!D); } -version (unittest) -{ - mixin template InputRangeStub() - { - @property int front() @nogc nothrow pure @safe - { - return 0; - } - @property bool empty() const @nogc nothrow pure @safe - { - return false; - } - void popFront() @nogc nothrow pure @safe - { - } - } - mixin template BidirectionalRangeStub() - { - @property int back() @nogc nothrow pure @safe - { - return 0; - } - void popBack() @nogc nothrow pure @safe - { - } - } -} - private template isDynamicArrayRange(R) { static if (is(R E : E[])) @@ -373,10 +355,12 @@ template isInputRange(R) void popFront() @nogc nothrow pure @safe { } + int front() @nogc nothrow pure @safe { return 0; } + bool empty() const @nogc nothrow pure @safe { return true; @@ -391,13 +375,8 @@ template isInputRange(R) { static struct Range1(T) { - void popFront() - { - } - int front() - { - return 0; - } + mixin InputRangeStub; + T empty() const { return true; @@ -408,45 +387,29 @@ template isInputRange(R) static struct Range2 { + mixin InputRangeStub; + int popFront() @nogc nothrow pure @safe { return 100; } - int front() @nogc nothrow pure @safe - { - return 100; - } - bool empty() const @nogc nothrow pure @safe - { - return true; - } } static assert(isInputRange!Range2); static struct Range3 { - void popFront() @nogc nothrow pure @safe - { - } + mixin InputRangeStub; + void front() @nogc nothrow pure @safe { } - bool empty() const @nogc nothrow pure @safe - { - return true; - } } static assert(!isInputRange!Range3); static struct Range4 { - void popFront() @nogc nothrow pure @safe - { - } - int front() @nogc nothrow pure @safe - { - return 0; - } + mixin InputRangeStub; + enum bool empty = false; } static assert(isInputRange!Range4); @@ -489,14 +452,17 @@ template isForwardRange(R) void popFront() @nogc nothrow pure @safe { } + int front() @nogc nothrow pure @safe { return 0; } + bool empty() const @nogc nothrow pure @safe { return true; } + typeof(this) save() @nogc nothrow pure @safe { return this; @@ -515,6 +481,7 @@ template isForwardRange(R) static struct Range2 { mixin InputRangeStub; + Range1 save() @nogc nothrow pure @safe { return Range1(); @@ -525,6 +492,7 @@ template isForwardRange(R) static struct Range3 { mixin InputRangeStub; + const(typeof(this)) save() const @nogc nothrow pure @safe { return this; @@ -573,21 +541,26 @@ template isBidirectionalRange(R) void popFront() @nogc nothrow pure @safe { } + void popBack() @nogc nothrow pure @safe { } + @property int front() @nogc nothrow pure @safe { return 0; } + @property int back() @nogc nothrow pure @safe { return 0; } + bool empty() const @nogc nothrow pure @safe { return true; } + Range save() @nogc nothrow pure @safe { return this; @@ -602,28 +575,17 @@ template isBidirectionalRange(R) { static struct Range(T, U) { - void popFront() @nogc nothrow pure @safe - { - } - void popBack() @nogc nothrow pure @safe - { - } + mixin BidirectionalRangeStub; + @property T front() @nogc nothrow pure @safe { return T.init; } + @property U back() @nogc nothrow pure @safe { return U.init; } - bool empty() const @nogc nothrow pure @safe - { - return true; - } - Range save() @nogc nothrow pure @safe - { - return this; - } } static assert(!isBidirectionalRange!(Range!(int, uint))); static assert(!isBidirectionalRange!(Range!(int, const int))); @@ -674,29 +636,22 @@ template isRandomAccessRange(R) void popFront() @nogc nothrow pure @safe { } - void popBack() @nogc nothrow pure @safe - { - } + @property int front() @nogc nothrow pure @safe { return 0; } - @property int back() @nogc nothrow pure @safe - { - return 0; - } + bool empty() const @nogc nothrow pure @safe { return true; } - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - int opIndex(const size_t pos) @nogc nothrow pure @safe + + int opIndex(size_t) @nogc nothrow pure @safe { return 0; } + size_t length() const @nogc nothrow pure @safe { return 0; @@ -711,15 +666,14 @@ template isRandomAccessRange(R) void popFront() @nogc nothrow pure @safe { } + @property int front() @nogc nothrow pure @safe { return 0; } + enum bool empty = false; - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } + int opIndex(const size_t pos) @nogc nothrow pure @safe { return 0; @@ -732,76 +686,43 @@ template isRandomAccessRange(R) { static struct Range1 { - mixin InputRangeStub; mixin BidirectionalRangeStub; - - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - int opIndex(const size_t pos) @nogc nothrow pure @safe - { - return 0; - } + mixin RandomAccessRangeStub; } static assert(!isRandomAccessRange!Range1); + @Length static struct Range2(Args...) { - mixin InputRangeStub; mixin BidirectionalRangeStub; - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } int opIndex(Args) @nogc nothrow pure @safe { return 0; } - size_t length() const @nogc nothrow pure @safe - { - return 0; - } } static assert(isRandomAccessRange!(Range2!size_t)); static assert(!isRandomAccessRange!(Range2!())); static assert(!isRandomAccessRange!(Range2!(size_t, size_t))); + @Length static struct Range3 { - mixin InputRangeStub; mixin BidirectionalRangeStub; - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } int opIndex(const size_t pos1, const size_t pos2 = 0) @nogc nothrow pure @safe { return 0; } - size_t length() const @nogc nothrow pure @safe - { - return 0; - } } static assert(isRandomAccessRange!Range3); static struct Range4 { - mixin InputRangeStub; mixin BidirectionalRangeStub; + mixin RandomAccessRangeStub; - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - int opIndex(const size_t pos1) @nogc nothrow pure @safe - { - return 0; - } size_t opDollar() const @nogc nothrow pure @safe { return 0; @@ -1097,28 +1018,20 @@ template isInfinite(R) @nogc nothrow pure @safe unittest { + @Infinite static struct StaticConstRange { - void popFront() @nogc nothrow pure @safe - { - } - @property int front() @nogc nothrow pure @safe - { - return 0; - } + mixin InputRangeStub; + static bool empty = false; } static assert(!isInfinite!StaticConstRange); + @Infinite static struct TrueRange { - void popFront() @nogc nothrow pure @safe - { - } - @property int front() @nogc nothrow pure @safe - { - return 0; - } + mixin InputRangeStub; + static const bool empty = true; } static assert(!isInfinite!TrueRange); @@ -1348,15 +1261,12 @@ if (isBidirectionalRange!R) @nogc nothrow pure @safe unittest { + @Infinite static struct InfiniteRange { + mixin ForwardRangeStub; private int i; - InfiniteRange save() @nogc nothrow pure @safe - { - return this; - } - void popFront() @nogc nothrow pure @safe { ++this.i; @@ -1376,8 +1286,6 @@ if (isBidirectionalRange!R) { return this.i; } - - enum bool empty = false; } { InfiniteRange range; @@ -1497,44 +1405,19 @@ if (isInputRange!R) @nogc nothrow pure @safe unittest { - static struct Element - { - this(this) @nogc nothrow pure @safe - { - assert(false); - } - } - // Returns its elements by reference. + @Infinite @WithLvalueElements static struct R1 { - Element element; - enum bool empty = false; - - ref Element front() @nogc nothrow pure @safe - { - return element; - } - - void popFront() @nogc nothrow pure @safe - { - } + mixin InputRangeStub!AssertPostblit; } static assert(is(typeof(moveFront(R1())))); // Returns elements with a postblit constructor by value. moveFront fails. + @Infinite static struct R2 { - enum bool empty = false; - - Element front() @nogc nothrow pure @safe - { - return Element(); - } - - void popFront() @nogc nothrow pure @safe - { - } + mixin InputRangeStub!AssertPostblit; } static assert(!is(typeof(moveFront(R2())))); } @@ -1582,58 +1465,19 @@ if (isBidirectionalRange!R) @nogc nothrow pure @safe unittest { - static struct Element - { - this(this) @nogc nothrow pure @safe - { - assert(false); - } - } - // Returns its elements by reference. + @Infinite @WithLvalueElements static struct R1 { - Element element; - enum bool empty = false; - - ref Element back() @nogc nothrow pure @safe - { - return element; - } - alias front = back; - - void popBack() @nogc nothrow pure @safe - { - } - alias popFront = popBack; - - R1 save() @nogc nothrow pure @safe - { - return this; - } + mixin BidirectionalRangeStub!AssertPostblit; } static assert(is(typeof(moveBack(R1())))); // Returns elements with a postblit constructor by value. moveBack fails. + @Infinite static struct R2 { - enum bool empty = false; - - Element back() @nogc nothrow pure @safe - { - return Element(); - } - alias front = back; - - void popBack() @nogc nothrow pure @safe - { - } - alias popFront = popBack; - - R2 save() @nogc nothrow pure @safe - { - return this; - } + mixin BidirectionalRangeStub!AssertPostblit; } static assert(!is(typeof(moveBack(R2())))); } @@ -1680,54 +1524,19 @@ if (isRandomAccessRange!R) @nogc nothrow pure @safe unittest { - static struct Element - { - this(this) @nogc nothrow pure @safe - { - assert(false); - } - } - // Returns its elements by reference. + @Infinite @WithLvalueElements static struct R1 { - Element element; - enum bool empty = false; - - ref Element front() @nogc nothrow pure @safe - { - return element; - } - - void popFront() @nogc nothrow pure @safe - { - } - - ref Element opIndex(size_t) - { - return element; - } + mixin RandomAccessRangeStub!AssertPostblit; } static assert(is(typeof(moveAt(R1(), 0)))); // Returns elements with a postblit constructor by value. moveAt fails. + @Infinite static struct R2 { - enum bool empty = false; - - Element front() @nogc nothrow pure @safe - { - return Element(); - } - - void popFront() @nogc nothrow pure @safe - { - } - - Element opIndex() @nogc nothrow pure @safe - { - return Element(); - } + mixin RandomAccessRangeStub!AssertPostblit; } static assert(!is(typeof(moveAt(R2(), 0)))); } diff --git a/source/tanya/test/package.d b/source/tanya/test/package.d index 2e34702..ab6f861 100644 --- a/source/tanya/test/package.d +++ b/source/tanya/test/package.d @@ -15,3 +15,4 @@ module tanya.test; public import tanya.test.assertion; +public import tanya.test.stub; diff --git a/source/tanya/test/range.d b/source/tanya/test/range.d deleted file mode 100644 index 92a8c05..0000000 --- a/source/tanya/test/range.d +++ /dev/null @@ -1,59 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Range generators for tests. - - * Copyright: Eugene Wissner 2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/range.d, - * tanya/test/range.d) - */ -module tanya.test.range; - -package(tanya) struct Empty -{ -} - -package(tanya) template InputRange() -{ - import tanya.meta.metafunction : AliasSeq; - - private alias attributes = AliasSeq!(__traits(getAttributes, typeof(this))); - - static foreach (attribute; attributes) - { - static if (is(attribute == Empty)) - { - @property bool empty() const @nogc nothrow pure @safe - { - return true; - } - } - } - - void popFront() @nogc nothrow pure @safe - { - static foreach (attribute; attributes) - { - static if (is(attribute == Empty)) - { - assert(false); - } - } - } - - int front() @nogc nothrow pure @safe - { - static foreach (attribute; attributes) - { - static if (is(attribute == Empty)) - { - assert(false); - } - } - } -} diff --git a/source/tanya/test/stub.d b/source/tanya/test/stub.d new file mode 100644 index 0000000..2be252e --- /dev/null +++ b/source/tanya/test/stub.d @@ -0,0 +1,275 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Range generators. + * + * Copyright: Eugene Wissner 2018. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/stub.d, + * tanya/test/stub.d) + */ +module tanya.test.stub; + +/** + * Attribute signalizing that the generated range should contain the given + * number of elements. + * + * $(D_PSYMBOL Count) should be always specified with some value and not as a + * type, so $(D_INLINECODE Count(1)) instead just $(D_INLINECODE Count), + * otherwise you can just omit $(D_PSYMBOL Count) and it will default to 0. + * + * $(D_PSYMBOL Count) doesn't generate `.length` property - use + * $(D_PSYMBOL Length) for that. + * + * If neither $(D_PSYMBOL Length) nor $(D_PSYMBOL Infinite) is given, + * $(D_ILNINECODE Count(0)) is assumed. + * + * This attribute conflicts with $(D_PSYMBOL Infinite) and $(D_PSYMBOL Length). + */ +struct Count +{ + /// Original range length. + size_t count = 0; + + @disable this(); + + /** + * Constructs the attribute with the given length. + * + * Params: + * count = Original range length. + */ + this(size_t count) @nogc nothrow pure @safe + { + this.count = count; + } +} + +/** + * Attribute signalizing that the generated range should be infinite. + * + * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Length). + */ +struct Infinite +{ +} + +/** + * Generates `.length` property for the range. + * + * The length of the range can be specified as a constructor argument, + * otherwise it is 0. + * + * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Infinite). + */ +struct Length +{ + /// Original range length. + size_t length = 0; +} + +/** + * Attribute signalizing that the generated range should return values by + * reference. + * + * This atribute affects the return values of `.front`, `.back` and `[]`. + */ +struct WithLvalueElements +{ +} + +/** + * Generates an input range. + * + * Params: + * E = Element type. + */ +mixin template InputRangeStub(E = int) +{ + import tanya.meta.metafunction : Alias; + import tanya.meta.trait : getUDAs, hasUDA; + + /* + * Aliases for the attribute lookups to access them faster + */ + private enum bool infinite = hasUDA!(typeof(this), Infinite); + private enum bool withLvalueElements = hasUDA!(typeof(this), + WithLvalueElements); + private alias Count = getUDAs!(typeof(this), .Count); + private alias Length = getUDAs!(typeof(this), .Length); + + static if (Count.length != 0) + { + private enum size_t count = Count[0].count; + + static assert (!infinite, + "Range cannot have count and be infinite at the same time"); + static assert (Length.length == 0, + "Range cannot have count and length at the same time"); + } + else static if (Length.length != 0) + { + private enum size_t count = evalUDA!(Length[0]).length; + + static assert (!infinite, + "Range cannot have length and be infinite at the same time"); + } + else static if (!infinite) + { + private enum size_t count = 0; + } + + /* + * Member generation + */ + static if (infinite) + { + enum bool empty = false; + } + else + { + private size_t length_ = count; + + @property bool empty() const @nogc nothrow pure @safe + { + return this.length_ == 0; + } + } + + static if (withLvalueElements) + { + private E* element; // Pointer to enable range copying in save() + } + + void popFront() @nogc nothrow pure @safe + in (!empty) + { + static if (!infinite) + { + --this.length_; + } + } + + static if (withLvalueElements) + { + ref E front() @nogc nothrow pure @safe + in (!empty) + { + return *this.element; + } + } + else + { + E front() @nogc nothrow pure @safe + in (!empty) + { + return E.init; + } + } + + static if (Length.length != 0) + { + size_t length() const @nogc nothrow pure @safe + { + return this.length_; + } + } +} + +/** + * Generates a forward range. + * + * This mixin includes input range primitives as well, but can be combined with + * $(D_PSYMBOL RandomAccessRangeStub). + * + * Params: + * E = Element type. + */ +mixin template ForwardRangeStub(E = int) +{ + static if (!is(typeof(this.InputRangeMixin) == void)) + { + mixin InputRangeStub!E InputRangeMixin; + } + + auto save() @nogc nothrow pure @safe + { + return this; + } +} + +/** + * Generates a bidirectional range. + * + * This mixin includes forward range primitives as well, but can be combined with + * $(D_PSYMBOL RandomAccessRangeStub). + * + * Params: + * E = Element type. + */ +mixin template BidirectionalRangeStub(E = int) +{ + mixin ForwardRangeStub!E; + + void popBack() @nogc nothrow pure @safe + in (!empty) + { + static if (!infinite) + { + --this.length_; + } + } + + static if (withLvalueElements) + { + ref E back() @nogc nothrow pure @safe + in (!empty) + { + return *this.element; + } + } + else + { + E back() @nogc nothrow pure @safe + in (!empty) + { + return E.init; + } + } +} + +/** + * Generates a random-access range. + * + * This mixin includes input range primitives as well, but can be combined with + * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). + * + * Params: + * E = Element type. + */ +mixin template RandomAccessRangeStub(E = int) +{ + static if (!is(typeof(this.InputRangeMixin) == void)) + { + mixin InputRangeStub!E InputRangeMixin; + } + + static if (withLvalueElements) + { + ref E opIndex(size_t) @nogc nothrow pure @safe + { + return *this.element; + } + } + else + { + E opIndex(size_t) @nogc nothrow pure @safe + { + return E.init; + } + } +}