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