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 `interface` definitions that can be used to
8 wrap around range objects created by the $(MREF std, range) templates.
10 $(SCRIPT inhibitQuickIndex = 1;)
13 $(TR $(TD $(LREF InputRange))
14 $(TD Wrapper for input ranges.
16 $(TR $(TD $(LREF InputAssignable))
17 $(TD Wrapper for input ranges with assignable elements.
19 $(TR $(TD $(LREF ForwardRange))
20 $(TD Wrapper for forward ranges.
22 $(TR $(TD $(LREF ForwardAssignable))
23 $(TD Wrapper for forward ranges with assignable elements.
25 $(TR $(TD $(LREF BidirectionalRange))
26 $(TD Wrapper for bidirectional ranges.
28 $(TR $(TD $(LREF BidirectionalAssignable))
29 $(TD Wrapper for bidirectional ranges with assignable elements.
31 $(TR $(TD $(LREF RandomAccessFinite))
32 $(TD Wrapper for finite random-access ranges.
34 $(TR $(TD $(LREF RandomAccessAssignable))
35 $(TD Wrapper for finite random-access ranges with assignable elements.
37 $(TR $(TD $(LREF RandomAccessInfinite))
38 $(TD Wrapper for infinite random-access ranges.
40 $(TR $(TD $(LREF OutputRange))
41 $(TD Wrapper for output ranges.
43 $(TR $(TD $(LREF OutputRangeObject))
44 $(TD Class that implements the `OutputRange` interface and wraps the
45 `put` methods in virtual functions.
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.
51 $(TR $(TD $(LREF InputRangeObject))
52 $(TD Class that implements the `InputRange` interface and wraps the
53 input range methods in virtual functions.
55 $(TR $(TD $(LREF inputRangeObject))
56 $(TD Convenience function for creating an `InputRangeObject`
59 $(TR $(TD $(LREF MostDerivedInputRange))
60 $(TD Returns the interface type that best matches the range.
65 Source: $(PHOBOSSRC std/range/interfaces.d)
67 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
69 Authors: $(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).
74 module std
.range
.interfaces
;
77 import std
.range
.primitives
;
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.
90 * These interfaces are not capable of forwarding `ref` access to elements.
92 * Infiniteness of the wrapped range is not propagated.
94 * Length is not propagated in the case of non-random access ranges.
97 * $(LREF inputRangeObject)
99 interface InputRange(E
) {
103 /**Calls $(REF moveFront, std, range, primitives) on the wrapped range, if
104 * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception.
112 @property bool empty();
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:
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)
123 /**`foreach` iteration uses opApply, since one delegate call per loop
124 * iteration is faster than three virtual function calls.
126 int opApply(scope int delegate(E
));
129 int opApply(scope int delegate(size_t
, E
));
136 import std
.algorithm
.iteration
: map
;
137 import std
.range
: iota
;
139 void useRange(InputRange
!int range
) {
143 // Create a range type.
144 auto squares
= map
!"a * a"(iota(10));
146 // Wrap it in an interface.
147 auto squaresWrapped
= inputRangeObject(squares
);
150 useRange(squaresWrapped
);
153 /**Interface for a forward range of type `E`.*/
154 interface ForwardRange(E
) : InputRange
!E
{
156 @property ForwardRange
!E
save();
159 /**Interface for a bidirectional range of type `E`.*/
160 interface BidirectionalRange(E
) : ForwardRange
!(E
) {
162 @property BidirectionalRange
!E
save();
167 /**Calls $(REF moveBack, std, range, primitives) on the wrapped range, if
168 * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception
176 /**Interface for a finite random access range of type `E`.*/
177 interface RandomAccessFinite(E
) : BidirectionalRange
!(E
) {
179 @property RandomAccessFinite
!E
save();
188 @property size_t
length();
191 alias opDollar
= length
;
193 // Can't support slicing until issues with requiring slicing for all
194 // finite random access ranges are fully resolved.
198 RandomAccessFinite
!E
opSlice(size_t
, size_t
);
202 /**Interface for an infinite random access range of type `E`.*/
203 interface RandomAccessInfinite(E
) : ForwardRange
!E
{
205 enum bool empty
= false;
207 /**Calls $(REF moveAt, std, range, primitives) on the wrapped range, if
208 * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception.
213 @property RandomAccessInfinite
!E
save();
219 // https://issues.dlang.org/show_bug.cgi?id=22608
222 static assert(isRandomAccessRange
!(RandomAccessInfinite
!int));
225 /**Adds assignable elements to InputRange.*/
226 interface InputAssignable(E
) : InputRange
!E
{
228 @property void front(E newVal
);
230 alias front
= InputRange
!E
.front
; // overload base interface method
235 static assert(isInputRange
!(InputAssignable
!int));
238 /**Adds assignable elements to ForwardRange.*/
239 interface ForwardAssignable(E
) : InputAssignable
!E
, ForwardRange
!E
{
241 @property ForwardAssignable
!E
save();
244 /**Adds assignable elements to BidirectionalRange.*/
245 interface BidirectionalAssignable(E
) : ForwardAssignable
!E
, BidirectionalRange
!E
{
247 @property BidirectionalAssignable
!E
save();
250 @property void back(E newVal
);
253 /**Adds assignable elements to RandomAccessFinite.*/
254 interface RandomFiniteAssignable(E
) : RandomAccessFinite
!E
, BidirectionalAssignable
!E
{
256 @property RandomFiniteAssignable
!E
save();
259 void opIndexAssign(E val
, size_t index
);
262 /**Interface for an output range of type `E`. Usage is similar to the
263 * `InputRange` interface and descendants.*/
264 interface OutputRange(E
) {
269 // https://issues.dlang.org/show_bug.cgi?id=6973
272 static assert(isOutputRange
!(OutputRange
!int, int));
276 // CTFE function that generates mixin code for one put() method for each
278 private string
putMethods(E
...)()
280 import std
.conv
: to
;
284 foreach (ti
, Unused
; E
)
286 ret ~= "void put(E[" ~ to
!string(ti
) ~ "] e) { .put(_range, e); }";
292 /**Implements the `OutputRange` interface for all types E and wraps the
293 * `put` method for each type `E` in a virtual function.
295 class 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.
305 mixin(putMethods
!E());
309 /**Returns the interface type that best matches `R`.*/
310 template MostDerivedInputRange(R
)
311 if (isInputRange
!(Unqual
!R
))
313 private alias E
= ElementType
!R
;
315 static if (isRandomAccessRange
!R
)
317 static if (isInfinite
!R
)
319 alias MostDerivedInputRange
= RandomAccessInfinite
!E
;
321 else static if (hasAssignableElements
!R
)
323 alias MostDerivedInputRange
= RandomFiniteAssignable
!E
;
327 alias MostDerivedInputRange
= RandomAccessFinite
!E
;
330 else static if (isBidirectionalRange
!R
)
332 static if (hasAssignableElements
!R
)
334 alias MostDerivedInputRange
= BidirectionalAssignable
!E
;
338 alias MostDerivedInputRange
= BidirectionalRange
!E
;
341 else static if (isForwardRange
!R
)
343 static if (hasAssignableElements
!R
)
345 alias MostDerivedInputRange
= ForwardAssignable
!E
;
349 alias MostDerivedInputRange
= ForwardRange
!E
;
354 static if (hasAssignableElements
!R
)
356 alias MostDerivedInputRange
= InputAssignable
!E
;
360 alias MostDerivedInputRange
= InputRange
!E
;
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.
369 template InputRangeObject(R
)
370 if (isInputRange
!(Unqual
!R
))
372 static if (is(R
: InputRange
!(ElementType
!R
)))
374 alias InputRangeObject
= R
;
376 else static if (!is(Unqual
!R
== R
))
378 alias InputRangeObject
= InputRangeObject
!(Unqual
!R
);
384 class InputRangeObject
: MostDerivedInputRange
!(R
) {
386 private alias E
= ElementType
!R
;
392 @property E
front() { return _range
.front
; }
395 static if (__traits(compiles
, _range
.moveFront()))
396 return _range
.moveFront();
398 throw new UnsupportedRangeMethod(
399 "Cannot move the front of a(n) `" ~ R
.stringof
~ "`");
402 void popFront() { _range
.popFront(); }
403 @property bool empty() { return _range
.empty
; }
405 static if (isForwardRange
!R
)
407 @property typeof(this) save() {
408 return new typeof(this)(_range
.save
);
412 static if (hasAssignableElements
!R
)
414 @property void front(E newVal
) {
415 _range
.front
= newVal
;
419 static if (isBidirectionalRange
!R
)
421 @property E
back() { return _range
.back
; }
424 static if (__traits(compiles
, _range
.moveFront()))
425 return _range
.moveBack();
427 throw new UnsupportedRangeMethod(
428 "Cannot move the back of a(n) `" ~ R
.stringof
~ "`");
431 void popBack() { return _range
.popBack(); }
433 static if (hasAssignableElements
!R
)
435 @property void back(E newVal
) {
436 _range
.back
= newVal
;
441 static if (isRandomAccessRange
!R
)
443 E
opIndex(size_t index
) {
444 return _range
[index
];
447 E
moveAt(size_t index
) {
448 static if (__traits(compiles
, _range
.moveAt(index
)))
449 return _range
.moveAt(index
);
451 throw new UnsupportedRangeMethod(
452 "Cannot move an element of a(n) `" ~ R
.stringof
~ "`");
455 static if (hasAssignableElements
!R
)
457 void opIndexAssign(E val
, size_t index
) {
462 static if (!isInfinite
!R
)
464 @property size_t
length() {
465 return _range
.length
;
468 alias opDollar
= length
;
470 // Can't support slicing until all the issues with
471 // requiring slicing support for finite random access
472 // ranges are resolved.
475 typeof(this) opSlice(size_t lower
, size_t upper
) {
476 return new typeof(this)(_range
[lower
.. upper
]);
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
) {
487 for (auto r
= _range
; !r
.empty
; r
.popFront())
496 int opApply(scope int delegate(size_t
, E
) dg
) {
500 for (auto r
= _range
; !r
.empty
; r
.popFront())
502 res
= dg(i
, r
.front
);
513 /**Convenience function for creating an `InputRangeObject` of the proper type.
514 * See $(LREF InputRange) for an example.
516 InputRangeObject
!R
inputRangeObject(R
)(R range
)
519 static if (is(R
: InputRange
!(ElementType
!R
)))
525 return new InputRangeObject
!R(range
);
529 /**Convenience function for creating an `OutputRangeObject` with a base range
530 * of type `R` that accepts types `E`.
532 template outputRangeObject(E
...) {
535 OutputRangeObject
!(R
, E
) outputRangeObject(R
)(R range
) {
536 return new OutputRangeObject
!(R
, E
)(range
);
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)));
550 /// Thrown when an interface method is not supported by the wrapped range
551 class UnsupportedRangeMethod
: Exception
553 import std
.exception
: basicExceptionCtors
;
555 mixin basicExceptionCtors
;
560 import std
.algorithm
.comparison
: equal
;
562 import std
.internal
.test.dummyrange
;
564 static void testEquality(R
)(iInputRange r1
, R r2
) {
565 assert(equal(r1
, r2
));
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
)));
575 assert(arr
.moveFront() == 0);
576 assert(arr
.moveBack() == 4);
577 assert(arr
.moveAt(1) == 2);
579 foreach (elem
; arrWrapped
) {}
580 foreach (i
, elem
; arrWrapped
) {}
582 assert(inputRangeObject(arrWrapped
) is arrWrapped
);
584 foreach (DummyType
; AllDummyRanges
)
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
));
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)));
602 appWrapped
.put([2, 3]);
603 assert(app
.data
.length
== 3);
604 assert(equal(app
.data
, [1,2,3]));
607 // https://issues.dlang.org/show_bug.cgi?id=19544
610 import std
.range
: repeat
;
614 inout this(ref inout typeof(this)) {}
617 auto r
= repeat(HasCC
.init
).inputRangeObject
;