xref: /netbsd-src/external/gpl3/gcc.old/dist/libphobos/src/std/range/interfaces.d (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /**
2 This module is a submodule of $(MREF std, range).
3 
4 The main $(MREF std, range) module provides template-based tools for working with
5 ranges, but sometimes an object-based interface for ranges is needed, such as
6 when runtime polymorphism is required. For this purpose, this submodule
7 provides a number of object and $(D interface) definitions that can be used to
8 wrap 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 
63 Source: $(PHOBOSSRC std/range/_interfaces.d)
64 
65 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
66 
67 Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha,
68 and Jonathan M Davis. Credit for some of the ideas in building this module goes
69 to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
70 */
71 module std.range.interfaces;
72 
73 import std.meta;
74 import std.range.primitives;
75 import 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  */
96 interface 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).*/
149 interface ForwardRange(E) : InputRange!E {
150     ///
151     @property ForwardRange!E save();
152 }
153 
154 /**Interface for a bidirectional range of type $(D E).*/
155 interface 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).*/
170 interface 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).*/
196 interface 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.*/
208 interface 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.*/
221 interface ForwardAssignable(E) : InputAssignable!E, ForwardRange!E {
222     ///
223     @property ForwardAssignable!E save();
224 }
225 
226 /**Adds assignable elements to BidirectionalRange.*/
227 interface 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.*/
236 interface 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.*/
246 interface 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.
260 private 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  */
277 class 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).*/
292 template MostDerivedInputRange(R)
293 if (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  */
351 template InputRangeObject(R)
352 if (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  */
486 InputRangeObject!R inputRangeObject(R)(R range)
487 if (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 */
502 template 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