]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/src/std/range/interfaces.d
Add D front-end, libphobos library, and D2 testsuite.
[thirdparty/gcc.git] / libphobos / src / std / range / interfaces.d
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 }