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