2 This module is a submodule of $(MREF std, range).
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.
10 $(SCRIPT inhibitQuickIndex = 1;)
12 $(TR $(TD $(LREF InputRange))
13 $(TD Wrapper for input ranges.
15 $(TR $(TD $(LREF InputAssignable))
16 $(TD Wrapper for input ranges with assignable elements.
18 $(TR $(TD $(LREF ForwardRange))
19 $(TD Wrapper for forward ranges.
21 $(TR $(TD $(LREF ForwardAssignable))
22 $(TD Wrapper for forward ranges with assignable elements.
24 $(TR $(TD $(LREF BidirectionalRange))
25 $(TD Wrapper for bidirectional ranges.
27 $(TR $(TD $(LREF BidirectionalAssignable))
28 $(TD Wrapper for bidirectional ranges with assignable elements.
30 $(TR $(TD $(LREF RandomAccessFinite))
31 $(TD Wrapper for finite random-access ranges.
33 $(TR $(TD $(LREF RandomAccessAssignable))
34 $(TD Wrapper for finite random-access ranges with assignable elements.
36 $(TR $(TD $(LREF RandomAccessInfinite))
37 $(TD Wrapper for infinite random-access ranges.
39 $(TR $(TD $(LREF OutputRange))
40 $(TD Wrapper for output ranges.
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.
49 $(TR $(TD $(LREF InputRangeObject))
50 $(TD Class that implements the $(D InputRange) interface and wraps the
51 input _range methods in virtual functions.
53 $(TR $(TD $(LREF inputRangeObject))
54 $(TD Convenience function for creating an $(D InputRangeObject)
57 $(TR $(TD $(LREF MostDerivedInputRange))
58 $(TD Returns the interface type that best matches the range.)
63 Source: $(PHOBOSSRC std/range/_interfaces.d)
65 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
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).
71 module std.range.interfaces;
74 import std.range.primitives;
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.
87 * These interfaces are not capable of forwarding $(D ref) access to elements.
89 * Infiniteness of the wrapped range is not propagated.
91 * Length is not propagated in the case of non-random access ranges.
94 * $(LREF inputRangeObject)
96 interface InputRange(E) {
107 @property bool empty();
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:
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)
118 /**$(D foreach) iteration uses opApply, since one delegate call per loop
119 * iteration is faster than three virtual function calls.
121 int opApply(scope int delegate(E));
124 int opApply(scope int delegate(size_t, E));
131 import std.algorithm.iteration : map;
132 import std.range : iota;
134 void useRange(InputRange!int range) {
138 // Create a range type.
139 auto squares = map!"a * a"(iota(10));
141 // Wrap it in an interface.
142 auto squaresWrapped = inputRangeObject(squares);
145 useRange(squaresWrapped);
148 /**Interface for a forward range of type $(D E).*/
149 interface ForwardRange(E) : InputRange!E {
151 @property ForwardRange!E save();
154 /**Interface for a bidirectional range of type $(D E).*/
155 interface BidirectionalRange(E) : ForwardRange!(E) {
157 @property BidirectionalRange!E save();
169 /**Interface for a finite random access range of type $(D E).*/
170 interface RandomAccessFinite(E) : BidirectionalRange!(E) {
172 @property RandomAccessFinite!E save();
181 @property size_t length();
184 alias opDollar = length;
186 // Can't support slicing until issues with requiring slicing for all
187 // finite random access ranges are fully resolved.
191 RandomAccessFinite!E opSlice(size_t, size_t);
195 /**Interface for an infinite random access range of type $(D E).*/
196 interface RandomAccessInfinite(E) : ForwardRange!E {
201 @property RandomAccessInfinite!E save();
207 /**Adds assignable elements to InputRange.*/
208 interface InputAssignable(E) : InputRange!E {
210 @property void front(E newVal);
212 alias front = InputRange!E.front; // overload base interface method
217 static assert(isInputRange!(InputAssignable!int));
220 /**Adds assignable elements to ForwardRange.*/
221 interface ForwardAssignable(E) : InputAssignable!E, ForwardRange!E {
223 @property ForwardAssignable!E save();
226 /**Adds assignable elements to BidirectionalRange.*/
227 interface BidirectionalAssignable(E) : ForwardAssignable!E, BidirectionalRange!E {
229 @property BidirectionalAssignable!E save();
232 @property void back(E newVal);
235 /**Adds assignable elements to RandomAccessFinite.*/
236 interface RandomFiniteAssignable(E) : RandomAccessFinite!E, BidirectionalAssignable!E {
238 @property RandomFiniteAssignable!E save();
241 void opIndexAssign(E val, size_t index);
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) {
254 static assert(isOutputRange!(OutputRange!int, int));
258 // CTFE function that generates mixin code for one put() method for each
260 private string putMethods(E...)()
262 import std.conv : to;
266 foreach (ti, Unused; E)
268 ret ~= "void put(E[" ~ to!string(ti) ~ "] e) { .put(_range, e); }";
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.
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.
287 mixin(putMethods!E());
291 /**Returns the interface type that best matches $(D R).*/
292 template MostDerivedInputRange(R)
293 if (isInputRange!(Unqual!R))
295 private alias E = ElementType!R;
297 static if (isRandomAccessRange!R)
299 static if (isInfinite!R)
301 alias MostDerivedInputRange = RandomAccessInfinite!E;
303 else static if (hasAssignableElements!R)
305 alias MostDerivedInputRange = RandomFiniteAssignable!E;
309 alias MostDerivedInputRange = RandomAccessFinite!E;
312 else static if (isBidirectionalRange!R)
314 static if (hasAssignableElements!R)
316 alias MostDerivedInputRange = BidirectionalAssignable!E;
320 alias MostDerivedInputRange = BidirectionalRange!E;
323 else static if (isForwardRange!R)
325 static if (hasAssignableElements!R)
327 alias MostDerivedInputRange = ForwardAssignable!E;
331 alias MostDerivedInputRange = ForwardRange!E;
336 static if (hasAssignableElements!R)
338 alias MostDerivedInputRange = InputAssignable!E;
342 alias MostDerivedInputRange = InputRange!E;
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.
351 template InputRangeObject(R)
352 if (isInputRange!(Unqual!R))
354 static if (is(R : InputRange!(ElementType!R)))
356 alias InputRangeObject = R;
358 else static if (!is(Unqual!R == R))
360 alias InputRangeObject = InputRangeObject!(Unqual!R);
366 class InputRangeObject : MostDerivedInputRange!(R) {
368 private alias E = ElementType!R;
374 @property E front() { return _range.front; }
377 return _range.moveFront();
380 void popFront() { _range.popFront(); }
381 @property bool empty() { return _range.empty; }
383 static if (isForwardRange!R)
385 @property typeof(this) save() {
386 return new typeof(this)(_range.save);
390 static if (hasAssignableElements!R)
392 @property void front(E newVal) {
393 _range.front = newVal;
397 static if (isBidirectionalRange!R)
399 @property E back() { return _range.back; }
402 return _range.moveBack();
405 void popBack() { return _range.popBack(); }
407 static if (hasAssignableElements!R)
409 @property void back(E newVal) {
410 _range.back = newVal;
415 static if (isRandomAccessRange!R)
417 E opIndex(size_t index) {
418 return _range[index];
421 E moveAt(size_t index) {
422 return _range.moveAt(index);
425 static if (hasAssignableElements!R)
427 void opIndexAssign(E val, size_t index) {
432 static if (!isInfinite!R)
434 @property size_t length() {
435 return _range.length;
438 alias opDollar = length;
440 // Can't support slicing until all the issues with
441 // requiring slicing support for finite random access
442 // ranges are resolved.
445 typeof(this) opSlice(size_t lower, size_t upper) {
446 return new typeof(this)(_range[lower .. upper]);
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) {
457 for (auto r = _range; !r.empty; r.popFront())
466 int opApply(scope int delegate(size_t, E) dg) {
470 for (auto r = _range; !r.empty; r.popFront())
472 res = dg(i, r.front);
483 /**Convenience function for creating an $(D InputRangeObject) of the proper type.
484 * See $(LREF InputRange) for an example.
486 InputRangeObject!R inputRangeObject(R)(R range)
489 static if (is(R : InputRange!(ElementType!R)))
495 return new InputRangeObject!R(range);
499 /**Convenience function for creating an $(D OutputRangeObject) with a base range
500 * of type $(D R) that accepts types $(D E).
502 template outputRangeObject(E...) {
505 OutputRangeObject!(R, E) outputRangeObject(R)(R range) {
506 return new OutputRangeObject!(R, E)(range);
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)));
522 import std.algorithm.comparison : equal;
524 import std.internal.test.dummyrange;
526 static void testEquality(R)(iInputRange r1, R r2) {
527 assert(equal(r1, r2));
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)));
537 assert(arr.moveFront() == 0);
538 assert(arr.moveBack() == 4);
539 assert(arr.moveAt(1) == 2);
541 foreach (elem; arrWrapped) {}
542 foreach (i, elem; arrWrapped) {}
544 assert(inputRangeObject(arrWrapped) is arrWrapped);
546 foreach (DummyType; AllDummyRanges)
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));
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)));
564 appWrapped.put([2, 3]);
565 assert(app.data.length == 3);
566 assert(equal(app.data, [1,2,3]));