|  | /** | 
|  | This module is a submodule of $(MREF std, range). | 
|  |  | 
|  | The main $(MREF std, range) module provides template-based tools for working with | 
|  | ranges, but sometimes an object-based interface for ranges is needed, such as | 
|  | when runtime polymorphism is required. For this purpose, this submodule | 
|  | provides a number of object and $(D interface) definitions that can be used to | 
|  | wrap around _range objects created by the $(MREF std, range) templates. | 
|  |  | 
|  | $(SCRIPT inhibitQuickIndex = 1;) | 
|  | $(BOOKTABLE , | 
|  | $(TR $(TD $(LREF InputRange)) | 
|  | $(TD Wrapper for input ranges. | 
|  | )) | 
|  | $(TR $(TD $(LREF InputAssignable)) | 
|  | $(TD Wrapper for input ranges with assignable elements. | 
|  | )) | 
|  | $(TR $(TD $(LREF ForwardRange)) | 
|  | $(TD Wrapper for forward ranges. | 
|  | )) | 
|  | $(TR $(TD $(LREF ForwardAssignable)) | 
|  | $(TD Wrapper for forward ranges with assignable elements. | 
|  | )) | 
|  | $(TR $(TD $(LREF BidirectionalRange)) | 
|  | $(TD Wrapper for bidirectional ranges. | 
|  | )) | 
|  | $(TR $(TD $(LREF BidirectionalAssignable)) | 
|  | $(TD Wrapper for bidirectional ranges with assignable elements. | 
|  | )) | 
|  | $(TR $(TD $(LREF RandomAccessFinite)) | 
|  | $(TD Wrapper for finite random-access ranges. | 
|  | )) | 
|  | $(TR $(TD $(LREF RandomAccessAssignable)) | 
|  | $(TD Wrapper for finite random-access ranges with assignable elements. | 
|  | )) | 
|  | $(TR $(TD $(LREF RandomAccessInfinite)) | 
|  | $(TD Wrapper for infinite random-access ranges. | 
|  | )) | 
|  | $(TR $(TD $(LREF OutputRange)) | 
|  | $(TD Wrapper for output ranges. | 
|  | )) | 
|  | $(TR $(TD $(LREF OutputRangeObject)) | 
|  | $(TD Class that implements the $(D OutputRange) interface and wraps the | 
|  | $(D put) methods in virtual functions. | 
|  | $(TR $(TD $(LREF outputRangeObject)) | 
|  | Convenience function for creating an $(D OutputRangeObject) with a base | 
|  | range of type R that accepts types E. | 
|  | )) | 
|  | $(TR $(TD $(LREF InputRangeObject)) | 
|  | $(TD Class that implements the $(D InputRange) interface and wraps the | 
|  | input _range methods in virtual functions. | 
|  | )) | 
|  | $(TR $(TD $(LREF inputRangeObject)) | 
|  | $(TD Convenience function for creating an $(D InputRangeObject) | 
|  | of the proper type. | 
|  | )) | 
|  | $(TR $(TD $(LREF MostDerivedInputRange)) | 
|  | $(TD Returns the interface type that best matches the range.) | 
|  | )) | 
|  | ) | 
|  |  | 
|  |  | 
|  | Source: $(PHOBOSSRC std/range/_interfaces.d) | 
|  |  | 
|  | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | 
|  |  | 
|  | Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, | 
|  | and Jonathan M Davis. Credit for some of the ideas in building this module goes | 
|  | to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). | 
|  | */ | 
|  | module std.range.interfaces; | 
|  |  | 
|  | import std.meta; | 
|  | import std.range.primitives; | 
|  | import std.traits; | 
|  |  | 
|  | /**These interfaces are intended to provide virtual function-based wrappers | 
|  | * around input ranges with element type E.  This is useful where a well-defined | 
|  | * binary interface is required, such as when a DLL function or virtual function | 
|  | * needs to accept a generic range as a parameter. Note that | 
|  | * $(REF_ALTTEXT isInputRange, isInputRange, std, range, primitives) | 
|  | * and friends check for conformance to structural interfaces | 
|  | * not for implementation of these $(D interface) types. | 
|  | * | 
|  | * Limitations: | 
|  | * | 
|  | * These interfaces are not capable of forwarding $(D ref) access to elements. | 
|  | * | 
|  | * Infiniteness of the wrapped range is not propagated. | 
|  | * | 
|  | * Length is not propagated in the case of non-random access ranges. | 
|  | * | 
|  | * See_Also: | 
|  | * $(LREF inputRangeObject) | 
|  | */ | 
|  | interface InputRange(E) { | 
|  | /// | 
|  | @property E front(); | 
|  |  | 
|  | /// | 
|  | E moveFront(); | 
|  |  | 
|  | /// | 
|  | void popFront(); | 
|  |  | 
|  | /// | 
|  | @property bool empty(); | 
|  |  | 
|  | /* Measurements of the benefits of using opApply instead of range primitives | 
|  | * for foreach, using timings for iterating over an iota(100_000_000) range | 
|  | * with an empty loop body, using the same hardware in each case: | 
|  | * | 
|  | * Bare Iota struct, range primitives:  278 milliseconds | 
|  | * InputRangeObject, opApply:           436 milliseconds  (1.57x penalty) | 
|  | * InputRangeObject, range primitives:  877 milliseconds  (3.15x penalty) | 
|  | */ | 
|  |  | 
|  | /**$(D foreach) iteration uses opApply, since one delegate call per loop | 
|  | * iteration is faster than three virtual function calls. | 
|  | */ | 
|  | int opApply(scope int delegate(E)); | 
|  |  | 
|  | /// Ditto | 
|  | int opApply(scope int delegate(size_t, E)); | 
|  |  | 
|  | } | 
|  |  | 
|  | /// | 
|  | @safe unittest | 
|  | { | 
|  | import std.algorithm.iteration : map; | 
|  | import std.range : iota; | 
|  |  | 
|  | void useRange(InputRange!int range) { | 
|  | // Function body. | 
|  | } | 
|  |  | 
|  | // Create a range type. | 
|  | auto squares = map!"a * a"(iota(10)); | 
|  |  | 
|  | // Wrap it in an interface. | 
|  | auto squaresWrapped = inputRangeObject(squares); | 
|  |  | 
|  | // Use it. | 
|  | useRange(squaresWrapped); | 
|  | } | 
|  |  | 
|  | /**Interface for a forward range of type $(D E).*/ | 
|  | interface ForwardRange(E) : InputRange!E { | 
|  | /// | 
|  | @property ForwardRange!E save(); | 
|  | } | 
|  |  | 
|  | /**Interface for a bidirectional range of type $(D E).*/ | 
|  | interface BidirectionalRange(E) : ForwardRange!(E) { | 
|  | /// | 
|  | @property BidirectionalRange!E save(); | 
|  |  | 
|  | /// | 
|  | @property E back(); | 
|  |  | 
|  | /// | 
|  | E moveBack(); | 
|  |  | 
|  | /// | 
|  | void popBack(); | 
|  | } | 
|  |  | 
|  | /**Interface for a finite random access range of type $(D E).*/ | 
|  | interface RandomAccessFinite(E) : BidirectionalRange!(E) { | 
|  | /// | 
|  | @property RandomAccessFinite!E save(); | 
|  |  | 
|  | /// | 
|  | E opIndex(size_t); | 
|  |  | 
|  | /// | 
|  | E moveAt(size_t); | 
|  |  | 
|  | /// | 
|  | @property size_t length(); | 
|  |  | 
|  | /// | 
|  | alias opDollar = length; | 
|  |  | 
|  | // Can't support slicing until issues with requiring slicing for all | 
|  | // finite random access ranges are fully resolved. | 
|  | version (none) | 
|  | { | 
|  | /// | 
|  | RandomAccessFinite!E opSlice(size_t, size_t); | 
|  | } | 
|  | } | 
|  |  | 
|  | /**Interface for an infinite random access range of type $(D E).*/ | 
|  | interface RandomAccessInfinite(E) : ForwardRange!E { | 
|  | /// | 
|  | E moveAt(size_t); | 
|  |  | 
|  | /// | 
|  | @property RandomAccessInfinite!E save(); | 
|  |  | 
|  | /// | 
|  | E opIndex(size_t); | 
|  | } | 
|  |  | 
|  | /**Adds assignable elements to InputRange.*/ | 
|  | interface InputAssignable(E) : InputRange!E { | 
|  | /// | 
|  | @property void front(E newVal); | 
|  |  | 
|  | alias front = InputRange!E.front; // overload base interface method | 
|  | } | 
|  |  | 
|  | @safe unittest | 
|  | { | 
|  | static assert(isInputRange!(InputAssignable!int)); | 
|  | } | 
|  |  | 
|  | /**Adds assignable elements to ForwardRange.*/ | 
|  | interface ForwardAssignable(E) : InputAssignable!E, ForwardRange!E { | 
|  | /// | 
|  | @property ForwardAssignable!E save(); | 
|  | } | 
|  |  | 
|  | /**Adds assignable elements to BidirectionalRange.*/ | 
|  | interface BidirectionalAssignable(E) : ForwardAssignable!E, BidirectionalRange!E { | 
|  | /// | 
|  | @property BidirectionalAssignable!E save(); | 
|  |  | 
|  | /// | 
|  | @property void back(E newVal); | 
|  | } | 
|  |  | 
|  | /**Adds assignable elements to RandomAccessFinite.*/ | 
|  | interface RandomFiniteAssignable(E) : RandomAccessFinite!E, BidirectionalAssignable!E { | 
|  | /// | 
|  | @property RandomFiniteAssignable!E save(); | 
|  |  | 
|  | /// | 
|  | void opIndexAssign(E val, size_t index); | 
|  | } | 
|  |  | 
|  | /**Interface for an output range of type $(D E).  Usage is similar to the | 
|  | * $(D InputRange) interface and descendants.*/ | 
|  | interface OutputRange(E) { | 
|  | /// | 
|  | void put(E); | 
|  | } | 
|  |  | 
|  | @safe unittest | 
|  | { | 
|  | // 6973 | 
|  | static assert(isOutputRange!(OutputRange!int, int)); | 
|  | } | 
|  |  | 
|  |  | 
|  | // CTFE function that generates mixin code for one put() method for each | 
|  | // type E. | 
|  | private string putMethods(E...)() | 
|  | { | 
|  | import std.conv : to; | 
|  |  | 
|  | string ret; | 
|  |  | 
|  | foreach (ti, Unused; E) | 
|  | { | 
|  | ret ~= "void put(E[" ~ to!string(ti) ~ "] e) { .put(_range, e); }"; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /**Implements the $(D OutputRange) interface for all types E and wraps the | 
|  | * $(D put) method for each type $(D E) in a virtual function. | 
|  | */ | 
|  | class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) { | 
|  | // @BUG 4689:  There should be constraints on this template class, but | 
|  | // DMD won't let me put them in. | 
|  | private R _range; | 
|  |  | 
|  | /// | 
|  | this(R range) { | 
|  | this._range = range; | 
|  | } | 
|  |  | 
|  | mixin(putMethods!E()); | 
|  | } | 
|  |  | 
|  |  | 
|  | /**Returns the interface type that best matches $(D R).*/ | 
|  | template MostDerivedInputRange(R) | 
|  | if (isInputRange!(Unqual!R)) | 
|  | { | 
|  | private alias E = ElementType!R; | 
|  |  | 
|  | static if (isRandomAccessRange!R) | 
|  | { | 
|  | static if (isInfinite!R) | 
|  | { | 
|  | alias MostDerivedInputRange = RandomAccessInfinite!E; | 
|  | } | 
|  | else static if (hasAssignableElements!R) | 
|  | { | 
|  | alias MostDerivedInputRange = RandomFiniteAssignable!E; | 
|  | } | 
|  | else | 
|  | { | 
|  | alias MostDerivedInputRange = RandomAccessFinite!E; | 
|  | } | 
|  | } | 
|  | else static if (isBidirectionalRange!R) | 
|  | { | 
|  | static if (hasAssignableElements!R) | 
|  | { | 
|  | alias MostDerivedInputRange = BidirectionalAssignable!E; | 
|  | } | 
|  | else | 
|  | { | 
|  | alias MostDerivedInputRange = BidirectionalRange!E; | 
|  | } | 
|  | } | 
|  | else static if (isForwardRange!R) | 
|  | { | 
|  | static if (hasAssignableElements!R) | 
|  | { | 
|  | alias MostDerivedInputRange = ForwardAssignable!E; | 
|  | } | 
|  | else | 
|  | { | 
|  | alias MostDerivedInputRange = ForwardRange!E; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | static if (hasAssignableElements!R) | 
|  | { | 
|  | alias MostDerivedInputRange = InputAssignable!E; | 
|  | } | 
|  | else | 
|  | { | 
|  | alias MostDerivedInputRange = InputRange!E; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /**Implements the most derived interface that $(D R) works with and wraps | 
|  | * all relevant range primitives in virtual functions.  If $(D R) is already | 
|  | * derived from the $(D InputRange) interface, aliases itself away. | 
|  | */ | 
|  | template InputRangeObject(R) | 
|  | if (isInputRange!(Unqual!R)) | 
|  | { | 
|  | static if (is(R : InputRange!(ElementType!R))) | 
|  | { | 
|  | alias InputRangeObject = R; | 
|  | } | 
|  | else static if (!is(Unqual!R == R)) | 
|  | { | 
|  | alias InputRangeObject = InputRangeObject!(Unqual!R); | 
|  | } | 
|  | else | 
|  | { | 
|  |  | 
|  | /// | 
|  | class InputRangeObject : MostDerivedInputRange!(R) { | 
|  | private R _range; | 
|  | private alias E = ElementType!R; | 
|  |  | 
|  | this(R range) { | 
|  | this._range = range; | 
|  | } | 
|  |  | 
|  | @property E front() { return _range.front; } | 
|  |  | 
|  | E moveFront() { | 
|  | return _range.moveFront(); | 
|  | } | 
|  |  | 
|  | void popFront() { _range.popFront(); } | 
|  | @property bool empty() { return _range.empty; } | 
|  |  | 
|  | static if (isForwardRange!R) | 
|  | { | 
|  | @property typeof(this) save() { | 
|  | return new typeof(this)(_range.save); | 
|  | } | 
|  | } | 
|  |  | 
|  | static if (hasAssignableElements!R) | 
|  | { | 
|  | @property void front(E newVal) { | 
|  | _range.front = newVal; | 
|  | } | 
|  | } | 
|  |  | 
|  | static if (isBidirectionalRange!R) | 
|  | { | 
|  | @property E back() { return _range.back; } | 
|  |  | 
|  | E moveBack() { | 
|  | return _range.moveBack(); | 
|  | } | 
|  |  | 
|  | void popBack() { return _range.popBack(); } | 
|  |  | 
|  | static if (hasAssignableElements!R) | 
|  | { | 
|  | @property void back(E newVal) { | 
|  | _range.back = newVal; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static if (isRandomAccessRange!R) | 
|  | { | 
|  | E opIndex(size_t index) { | 
|  | return _range[index]; | 
|  | } | 
|  |  | 
|  | E moveAt(size_t index) { | 
|  | return _range.moveAt(index); | 
|  | } | 
|  |  | 
|  | static if (hasAssignableElements!R) | 
|  | { | 
|  | void opIndexAssign(E val, size_t index) { | 
|  | _range[index] = val; | 
|  | } | 
|  | } | 
|  |  | 
|  | static if (!isInfinite!R) | 
|  | { | 
|  | @property size_t length() { | 
|  | return _range.length; | 
|  | } | 
|  |  | 
|  | alias opDollar = length; | 
|  |  | 
|  | // Can't support slicing until all the issues with | 
|  | // requiring slicing support for finite random access | 
|  | // ranges are resolved. | 
|  | version (none) | 
|  | { | 
|  | typeof(this) opSlice(size_t lower, size_t upper) { | 
|  | return new typeof(this)(_range[lower .. upper]); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Optimization:  One delegate call is faster than three virtual | 
|  | // function calls.  Use opApply for foreach syntax. | 
|  | int opApply(scope int delegate(E) dg) { | 
|  | int res; | 
|  |  | 
|  | for (auto r = _range; !r.empty; r.popFront()) | 
|  | { | 
|  | res = dg(r.front); | 
|  | if (res) break; | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | int opApply(scope int delegate(size_t, E) dg) { | 
|  | int res; | 
|  |  | 
|  | size_t i = 0; | 
|  | for (auto r = _range; !r.empty; r.popFront()) | 
|  | { | 
|  | res = dg(i, r.front); | 
|  | if (res) break; | 
|  | i++; | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /**Convenience function for creating an $(D InputRangeObject) of the proper type. | 
|  | * See $(LREF InputRange) for an example. | 
|  | */ | 
|  | InputRangeObject!R inputRangeObject(R)(R range) | 
|  | if (isInputRange!R) | 
|  | { | 
|  | static if (is(R : InputRange!(ElementType!R))) | 
|  | { | 
|  | return range; | 
|  | } | 
|  | else | 
|  | { | 
|  | return new InputRangeObject!R(range); | 
|  | } | 
|  | } | 
|  |  | 
|  | /**Convenience function for creating an $(D OutputRangeObject) with a base range | 
|  | * of type $(D R) that accepts types $(D E). | 
|  | */ | 
|  | template outputRangeObject(E...) { | 
|  |  | 
|  | /// | 
|  | OutputRangeObject!(R, E) outputRangeObject(R)(R range) { | 
|  | return new OutputRangeObject!(R, E)(range); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// | 
|  | @safe unittest | 
|  | { | 
|  | import std.array; | 
|  | auto app = appender!(uint[])(); | 
|  | auto appWrapped = outputRangeObject!(uint, uint[])(app); | 
|  | static assert(is(typeof(appWrapped) : OutputRange!(uint[]))); | 
|  | static assert(is(typeof(appWrapped) : OutputRange!(uint))); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | import std.algorithm.comparison : equal; | 
|  | import std.array; | 
|  | import std.internal.test.dummyrange; | 
|  |  | 
|  | static void testEquality(R)(iInputRange r1, R r2) { | 
|  | assert(equal(r1, r2)); | 
|  | } | 
|  |  | 
|  | auto arr = [1,2,3,4]; | 
|  | RandomFiniteAssignable!int arrWrapped = inputRangeObject(arr); | 
|  | static assert(isRandomAccessRange!(typeof(arrWrapped))); | 
|  | //    static assert(hasSlicing!(typeof(arrWrapped))); | 
|  | static assert(hasLength!(typeof(arrWrapped))); | 
|  | arrWrapped[0] = 0; | 
|  | assert(arr[0] == 0); | 
|  | assert(arr.moveFront() == 0); | 
|  | assert(arr.moveBack() == 4); | 
|  | assert(arr.moveAt(1) == 2); | 
|  |  | 
|  | foreach (elem; arrWrapped) {} | 
|  | foreach (i, elem; arrWrapped) {} | 
|  |  | 
|  | assert(inputRangeObject(arrWrapped) is arrWrapped); | 
|  |  | 
|  | foreach (DummyType; AllDummyRanges) | 
|  | { | 
|  | auto d = DummyType.init; | 
|  | static assert(propagatesRangeType!(DummyType, | 
|  | typeof(inputRangeObject(d)))); | 
|  | static assert(propagatesRangeType!(DummyType, | 
|  | MostDerivedInputRange!DummyType)); | 
|  | InputRange!uint wrapped = inputRangeObject(d); | 
|  | assert(equal(wrapped, d)); | 
|  | } | 
|  |  | 
|  | // Test output range stuff. | 
|  | auto app = appender!(uint[])(); | 
|  | auto appWrapped = outputRangeObject!(uint, uint[])(app); | 
|  | static assert(is(typeof(appWrapped) : OutputRange!(uint[]))); | 
|  | static assert(is(typeof(appWrapped) : OutputRange!(uint))); | 
|  |  | 
|  | appWrapped.put(1); | 
|  | appWrapped.put([2, 3]); | 
|  | assert(app.data.length == 3); | 
|  | assert(equal(app.data, [1,2,3])); | 
|  | } |