1/**
2This module is a submodule of $(MREF std, range).
3
4The main $(MREF std, range) module provides template-based tools for working with
5ranges, but sometimes an object-based interface for ranges is needed, such as
6when runtime polymorphism is required. For this purpose, this submodule
7provides a number of object and `interface` definitions that can be used to
8wrap around range objects created by the $(MREF std, range) templates.
9
10$(SCRIPT inhibitQuickIndex = 1;)
11$(DIVC quickindex,
12$(BOOKTABLE ,
13    $(TR $(TD $(LREF InputRange))
14        $(TD Wrapper for input ranges.
15    ))
16    $(TR $(TD $(LREF InputAssignable))
17        $(TD Wrapper for input ranges with assignable elements.
18    ))
19    $(TR $(TD $(LREF ForwardRange))
20        $(TD Wrapper for forward ranges.
21    ))
22    $(TR $(TD $(LREF ForwardAssignable))
23        $(TD Wrapper for forward ranges with assignable elements.
24    ))
25    $(TR $(TD $(LREF BidirectionalRange))
26        $(TD Wrapper for bidirectional ranges.
27    ))
28    $(TR $(TD $(LREF BidirectionalAssignable))
29        $(TD Wrapper for bidirectional ranges with assignable elements.
30    ))
31    $(TR $(TD $(LREF RandomAccessFinite))
32        $(TD Wrapper for finite random-access ranges.
33    ))
34    $(TR $(TD $(LREF RandomAccessAssignable))
35        $(TD Wrapper for finite random-access ranges with assignable elements.
36    ))
37    $(TR $(TD $(LREF RandomAccessInfinite))
38        $(TD Wrapper for infinite random-access ranges.
39    ))
40    $(TR $(TD $(LREF OutputRange))
41        $(TD Wrapper for output ranges.
42    ))
43    $(TR $(TD $(LREF OutputRangeObject))
44        $(TD Class that implements the `OutputRange` interface and wraps the
45        `put` methods in virtual functions.
46    ))
47    $(TR $(TD $(LREF outputRangeObject))
48        $(TD Convenience function for creating an `OutputRangeObject` with a base
49        range of type R that accepts types E.
50    ))
51    $(TR $(TD $(LREF InputRangeObject))
52        $(TD Class that implements the `InputRange` interface and wraps the
53        input range methods in virtual functions.
54    ))
55    $(TR $(TD $(LREF inputRangeObject))
56        $(TD Convenience function for creating an `InputRangeObject`
57        of the proper type.
58    ))
59    $(TR $(TD $(LREF MostDerivedInputRange))
60        $(TD Returns the interface type that best matches the range.
61    ))
62))
63
64
65Source: $(PHOBOSSRC std/range/interfaces.d)
66
67License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
68
69Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and
70         $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas
71         in building this module goes to
72         $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
73*/
74module std.range.interfaces;
75
76import std.meta;
77import std.range.primitives;
78import std.traits;
79
80/**These interfaces are intended to provide virtual function-based wrappers
81 * around input ranges with element type E.  This is useful where a well-defined
82 * binary interface is required, such as when a DLL function or virtual function
83 * needs to accept a generic range as a parameter. Note that
84 * $(REF_ALTTEXT isInputRange, isInputRange, std, range, primitives)
85 * and friends check for conformance to structural interfaces
86 * not for implementation of these `interface` types.
87 *
88 * Limitations:
89 *
90 * These interfaces are not capable of forwarding `ref` access to elements.
91 *
92 * Infiniteness of the wrapped range is not propagated.
93 *
94 * Length is not propagated in the case of non-random access ranges.
95 *
96 * See_Also:
97 * $(LREF inputRangeObject)
98 */
99interface InputRange(E) {
100    ///
101    @property E front();
102
103    /**Calls $(REF moveFront, std, range, primitives) on the wrapped range, if
104     * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception.
105     */
106    E moveFront();
107
108    ///
109    void popFront();
110
111    ///
112    @property bool empty();
113
114    /* Measurements of the benefits of using opApply instead of range primitives
115     * for foreach, using timings for iterating over an iota(100_000_000) range
116     * with an empty loop body, using the same hardware in each case:
117     *
118     * Bare Iota struct, range primitives:  278 milliseconds
119     * InputRangeObject, opApply:           436 milliseconds  (1.57x penalty)
120     * InputRangeObject, range primitives:  877 milliseconds  (3.15x penalty)
121     */
122
123    /**`foreach` iteration uses opApply, since one delegate call per loop
124     * iteration is faster than three virtual function calls.
125     */
126    int opApply(scope int delegate(E));
127
128    /// Ditto
129    int opApply(scope int delegate(size_t, E));
130
131}
132
133///
134@safe unittest
135{
136    import std.algorithm.iteration : map;
137    import std.range : iota;
138
139    void useRange(InputRange!int range) {
140        // Function body.
141    }
142
143    // Create a range type.
144    auto squares = map!"a * a"(iota(10));
145
146    // Wrap it in an interface.
147    auto squaresWrapped = inputRangeObject(squares);
148
149    // Use it.
150    useRange(squaresWrapped);
151}
152
153/**Interface for a forward range of type `E`.*/
154interface ForwardRange(E) : InputRange!E {
155    ///
156    @property ForwardRange!E save();
157}
158
159/**Interface for a bidirectional range of type `E`.*/
160interface BidirectionalRange(E) : ForwardRange!(E) {
161    ///
162    @property BidirectionalRange!E save();
163
164    ///
165    @property E back();
166
167    /**Calls $(REF moveBack, std, range, primitives) on the wrapped range, if
168     * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception
169     */
170    E moveBack();
171
172    ///
173    void popBack();
174}
175
176/**Interface for a finite random access range of type `E`.*/
177interface RandomAccessFinite(E) : BidirectionalRange!(E) {
178    ///
179    @property RandomAccessFinite!E save();
180
181    ///
182    E opIndex(size_t);
183
184    ///
185    E moveAt(size_t);
186
187    ///
188    @property size_t length();
189
190    ///
191    alias opDollar = length;
192
193    // Can't support slicing until issues with requiring slicing for all
194    // finite random access ranges are fully resolved.
195    version (none)
196    {
197        ///
198        RandomAccessFinite!E opSlice(size_t, size_t);
199    }
200}
201
202/**Interface for an infinite random access range of type `E`.*/
203interface RandomAccessInfinite(E) : ForwardRange!E {
204    ///
205    enum bool empty = false;
206
207    /**Calls $(REF moveAt, std, range, primitives) on the wrapped range, if
208     * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception.
209     */
210    E moveAt(size_t);
211
212    ///
213    @property RandomAccessInfinite!E save();
214
215    ///
216    E opIndex(size_t);
217}
218
219// https://issues.dlang.org/show_bug.cgi?id=22608
220@safe unittest
221{
222    static assert(isRandomAccessRange!(RandomAccessInfinite!int));
223}
224
225/**Adds assignable elements to InputRange.*/
226interface InputAssignable(E) : InputRange!E {
227    ///
228    @property void front(E newVal);
229
230    alias front = InputRange!E.front; // overload base interface method
231}
232
233@safe unittest
234{
235    static assert(isInputRange!(InputAssignable!int));
236}
237
238/**Adds assignable elements to ForwardRange.*/
239interface ForwardAssignable(E) : InputAssignable!E, ForwardRange!E {
240    ///
241    @property ForwardAssignable!E save();
242}
243
244/**Adds assignable elements to BidirectionalRange.*/
245interface BidirectionalAssignable(E) : ForwardAssignable!E, BidirectionalRange!E {
246    ///
247    @property BidirectionalAssignable!E save();
248
249    ///
250    @property void back(E newVal);
251}
252
253/**Adds assignable elements to RandomAccessFinite.*/
254interface RandomFiniteAssignable(E) : RandomAccessFinite!E, BidirectionalAssignable!E {
255    ///
256    @property RandomFiniteAssignable!E save();
257
258    ///
259    void opIndexAssign(E val, size_t index);
260}
261
262/**Interface for an output range of type `E`.  Usage is similar to the
263 * `InputRange` interface and descendants.*/
264interface OutputRange(E) {
265    ///
266    void put(E);
267}
268
269// https://issues.dlang.org/show_bug.cgi?id=6973
270@safe unittest
271{
272    static assert(isOutputRange!(OutputRange!int, int));
273}
274
275
276// CTFE function that generates mixin code for one put() method for each
277// type E.
278private string putMethods(E...)()
279{
280    import std.conv : to;
281
282    string ret;
283
284    foreach (ti, Unused; E)
285    {
286        ret ~= "void put(E[" ~ to!string(ti) ~ "] e) { .put(_range, e); }";
287    }
288
289    return ret;
290}
291
292/**Implements the `OutputRange` interface for all types E and wraps the
293 * `put` method for each type `E` in a virtual function.
294 */
295class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) {
296    // @BUG 4689:  There should be constraints on this template class, but
297    // DMD won't let me put them in.
298    private R _range;
299
300    ///
301    this(R range) {
302        this._range = range;
303    }
304
305    mixin(putMethods!E());
306}
307
308
309/**Returns the interface type that best matches `R`.*/
310template MostDerivedInputRange(R)
311if (isInputRange!(Unqual!R))
312{
313    private alias E = ElementType!R;
314
315    static if (isRandomAccessRange!R)
316    {
317        static if (isInfinite!R)
318        {
319            alias MostDerivedInputRange = RandomAccessInfinite!E;
320        }
321        else static if (hasAssignableElements!R)
322        {
323            alias MostDerivedInputRange = RandomFiniteAssignable!E;
324        }
325        else
326        {
327            alias MostDerivedInputRange = RandomAccessFinite!E;
328        }
329    }
330    else static if (isBidirectionalRange!R)
331    {
332        static if (hasAssignableElements!R)
333        {
334            alias MostDerivedInputRange = BidirectionalAssignable!E;
335        }
336        else
337        {
338            alias MostDerivedInputRange = BidirectionalRange!E;
339        }
340    }
341    else static if (isForwardRange!R)
342    {
343        static if (hasAssignableElements!R)
344        {
345            alias MostDerivedInputRange = ForwardAssignable!E;
346        }
347        else
348        {
349            alias MostDerivedInputRange = ForwardRange!E;
350        }
351    }
352    else
353    {
354        static if (hasAssignableElements!R)
355        {
356            alias MostDerivedInputRange = InputAssignable!E;
357        }
358        else
359        {
360            alias MostDerivedInputRange = InputRange!E;
361        }
362    }
363}
364
365/**Implements the most derived interface that `R` works with and wraps
366 * all relevant range primitives in virtual functions.  If `R` is already
367 * derived from the `InputRange` interface, aliases itself away.
368 */
369template InputRangeObject(R)
370if (isInputRange!(Unqual!R))
371{
372    static if (is(R : InputRange!(ElementType!R)))
373    {
374        alias InputRangeObject = R;
375    }
376    else static if (!is(Unqual!R == R))
377    {
378        alias InputRangeObject = InputRangeObject!(Unqual!R);
379    }
380    else
381    {
382
383        ///
384        class InputRangeObject : MostDerivedInputRange!(R) {
385            private R _range;
386            private alias E = ElementType!R;
387
388            this(R range) {
389                this._range = range;
390            }
391
392            @property E front() { return _range.front; }
393
394            E moveFront() {
395                static if (__traits(compiles, _range.moveFront()))
396                    return _range.moveFront();
397                else
398                    throw new UnsupportedRangeMethod(
399                        "Cannot move the front of a(n) `" ~ R.stringof ~ "`");
400            }
401
402            void popFront() { _range.popFront(); }
403            @property bool empty() { return _range.empty; }
404
405            static if (isForwardRange!R)
406            {
407                @property typeof(this) save() {
408                    return new typeof(this)(_range.save);
409                }
410            }
411
412            static if (hasAssignableElements!R)
413            {
414                @property void front(E newVal) {
415                    _range.front = newVal;
416                }
417            }
418
419            static if (isBidirectionalRange!R)
420            {
421                @property E back() { return _range.back; }
422
423                E moveBack() {
424                    static if (__traits(compiles, _range.moveFront()))
425                        return _range.moveBack();
426                    else
427                        throw new UnsupportedRangeMethod(
428                            "Cannot move the back of a(n) `" ~ R.stringof ~ "`");
429                }
430
431                void popBack() { return _range.popBack(); }
432
433                static if (hasAssignableElements!R)
434                {
435                    @property void back(E newVal) {
436                        _range.back = newVal;
437                    }
438                }
439            }
440
441            static if (isRandomAccessRange!R)
442            {
443                E opIndex(size_t index) {
444                    return _range[index];
445                }
446
447                E moveAt(size_t index) {
448                    static if (__traits(compiles, _range.moveAt(index)))
449                        return _range.moveAt(index);
450                    else
451                        throw new UnsupportedRangeMethod(
452                            "Cannot move an element of a(n) `" ~ R.stringof ~ "`");
453                }
454
455                static if (hasAssignableElements!R)
456                {
457                    void opIndexAssign(E val, size_t index) {
458                        _range[index] = val;
459                    }
460                }
461
462                static if (!isInfinite!R)
463                {
464                    @property size_t length() {
465                        return _range.length;
466                    }
467
468                    alias opDollar = length;
469
470                    // Can't support slicing until all the issues with
471                    // requiring slicing support for finite random access
472                    // ranges are resolved.
473                    version (none)
474                    {
475                        typeof(this) opSlice(size_t lower, size_t upper) {
476                            return new typeof(this)(_range[lower .. upper]);
477                        }
478                    }
479                }
480            }
481
482            // Optimization:  One delegate call is faster than three virtual
483            // function calls.  Use opApply for foreach syntax.
484            int opApply(scope int delegate(E) dg) {
485                int res;
486
487                for (auto r = _range; !r.empty; r.popFront())
488                {
489                    res = dg(r.front);
490                    if (res) break;
491                }
492
493                return res;
494            }
495
496            int opApply(scope int delegate(size_t, E) dg) {
497                int res;
498
499                size_t i = 0;
500                for (auto r = _range; !r.empty; r.popFront())
501                {
502                    res = dg(i, r.front);
503                    if (res) break;
504                    i++;
505                }
506
507                return res;
508            }
509        }
510    }
511}
512
513/**Convenience function for creating an `InputRangeObject` of the proper type.
514 * See $(LREF InputRange) for an example.
515 */
516InputRangeObject!R inputRangeObject(R)(R range)
517if (isInputRange!R)
518{
519    static if (is(R : InputRange!(ElementType!R)))
520    {
521        return range;
522    }
523    else
524    {
525        return new InputRangeObject!R(range);
526    }
527}
528
529/**Convenience function for creating an `OutputRangeObject` with a base range
530 * of type `R` that accepts types `E`.
531*/
532template outputRangeObject(E...) {
533
534    ///
535    OutputRangeObject!(R, E) outputRangeObject(R)(R range) {
536        return new OutputRangeObject!(R, E)(range);
537    }
538}
539
540///
541@safe unittest
542{
543     import std.array;
544     auto app = appender!(uint[])();
545     auto appWrapped = outputRangeObject!(uint, uint[])(app);
546     static assert(is(typeof(appWrapped) : OutputRange!(uint[])));
547     static assert(is(typeof(appWrapped) : OutputRange!(uint)));
548}
549
550/// Thrown when an interface method is not supported by the wrapped range
551class UnsupportedRangeMethod : Exception
552{
553    import std.exception : basicExceptionCtors;
554
555    mixin basicExceptionCtors;
556}
557
558@system unittest
559{
560    import std.algorithm.comparison : equal;
561    import std.array;
562    import std.internal.test.dummyrange;
563
564    static void testEquality(R)(iInputRange r1, R r2) {
565        assert(equal(r1, r2));
566    }
567
568    auto arr = [1,2,3,4];
569    RandomFiniteAssignable!int arrWrapped = inputRangeObject(arr);
570    static assert(isRandomAccessRange!(typeof(arrWrapped)));
571    //    static assert(hasSlicing!(typeof(arrWrapped)));
572    static assert(hasLength!(typeof(arrWrapped)));
573    arrWrapped[0] = 0;
574    assert(arr[0] == 0);
575    assert(arr.moveFront() == 0);
576    assert(arr.moveBack() == 4);
577    assert(arr.moveAt(1) == 2);
578
579    foreach (elem; arrWrapped) {}
580    foreach (i, elem; arrWrapped) {}
581
582    assert(inputRangeObject(arrWrapped) is arrWrapped);
583
584    foreach (DummyType; AllDummyRanges)
585    {
586        auto d = DummyType.init;
587        static assert(propagatesRangeType!(DummyType,
588                        typeof(inputRangeObject(d))));
589        static assert(propagatesRangeType!(DummyType,
590                        MostDerivedInputRange!DummyType));
591        InputRange!uint wrapped = inputRangeObject(d);
592        assert(equal(wrapped, d));
593    }
594
595    // Test output range stuff.
596    auto app = appender!(uint[])();
597    auto appWrapped = outputRangeObject!(uint, uint[])(app);
598    static assert(is(typeof(appWrapped) : OutputRange!(uint[])));
599    static assert(is(typeof(appWrapped) : OutputRange!(uint)));
600
601    appWrapped.put(1);
602    appWrapped.put([2, 3]);
603    assert(app.data.length == 3);
604    assert(equal(app.data, [1,2,3]));
605}
606
607// https://issues.dlang.org/show_bug.cgi?id=19544
608@safe unittest
609{
610    import std.range : repeat;
611
612    static struct HasCC
613    {
614        inout this(ref inout typeof(this)) {}
615    }
616
617    auto r = repeat(HasCC.init).inputRangeObject;
618}
619