/**This module is a submodule of $(MREF std, range). It defines the bidirectional and forward range primitives for arrays:$(LREF empty), $(LREF front), $(LREF back), $(LREF popFront), $(LREF popBack) and $(LREF save). It provides basic range functionality by defining several templates for testingwhether a given object is a range, and what kind of range it is: $(SCRIPT inhibitQuickIndex = 1;)$(DIVC quickindex,$(BOOKTABLE , $(TR $(TD $(LREF isInputRange)) $(TD Tests if something is an $(I input range), defined to be something from which one can sequentially read data using the primitives `front`, `popFront`, and `empty`. )) $(TR $(TD $(LREF isOutputRange)) $(TD Tests if something is an $(I output range), defined to be something to which one can sequentially write data using the $(LREF put) primitive. )) $(TR $(TD $(LREF isForwardRange)) $(TD Tests if something is a $(I forward range), defined to be an input range with the additional capability that one can save one's current position with the `save` primitive, thus allowing one to iterate over the same range multiple times. )) $(TR $(TD $(LREF isBidirectionalRange)) $(TD Tests if something is a $(I bidirectional range), that is, a forward range that allows reverse traversal using the primitives $(D back) and `popBack`. )) $(TR $(TD $(LREF isRandomAccessRange)) $(TD Tests if something is a $(I random access range), which is a bidirectional range that also supports the array subscripting operation via the primitive `opIndex`. )))) It also provides number of templates that test for various range capabilities: $(BOOKTABLE , $(TR $(TD $(LREF hasMobileElements)) $(TD Tests if a given range's elements can be moved around using the primitives `moveFront`, `moveBack`, or `moveAt`. )) $(TR $(TD $(LREF ElementType)) $(TD Returns the element type of a given range. )) $(TR $(TD $(LREF ElementEncodingType)) $(TD Returns the encoding element type of a given range. )) $(TR $(TD $(LREF hasSwappableElements)) $(TD Tests if a range is a forward range with swappable elements. )) $(TR $(TD $(LREF hasAssignableElements)) $(TD Tests if a range is a forward range with mutable elements. )) $(TR $(TD $(LREF hasLvalueElements)) $(TD Tests if a range is a forward range with elements that can be passed by reference and have their address taken. )) $(TR $(TD $(LREF hasLength)) $(TD Tests if a given range has the `length` attribute. )) $(TR $(TD $(LREF isInfinite)) $(TD Tests if a given range is an $(I infinite range). )) $(TR $(TD $(LREF hasSlicing)) $(TD Tests if a given range supports the array slicing operation $(D R[x .. y]). ))) Finally, it includes some convenience functions for manipulating ranges: $(BOOKTABLE , $(TR $(TD $(LREF popFrontN)) $(TD Advances a given range by up to $(I n) elements. )) $(TR $(TD $(LREF popBackN)) $(TD Advances a given bidirectional range from the right by up to $(I n) elements. )) $(TR $(TD $(LREF popFrontExactly)) $(TD Advances a given range by up exactly $(I n) elements. )) $(TR $(TD $(LREF popBackExactly)) $(TD Advances a given bidirectional range from the right by exactly $(I n) elements. )) $(TR $(TD $(LREF moveFront)) $(TD Removes the front element of a range. )) $(TR $(TD $(LREF moveBack)) $(TD Removes the back element of a bidirectional range. )) $(TR $(TD $(LREF moveAt)) $(TD Removes the $(I i)'th element of a random-access range. )) $(TR $(TD $(LREF walkLength)) $(TD Computes the length of any range in O(n) time. )) $(TR $(TD $(LREF put)) $(TD Outputs element `e` to a range. ))) Source: $(PHOBOSSRC std/range/primitives.d) License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas in building this module goes to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).*/module std.range.primitives; import std.traits; /**Returns `true` if `R` is an input range. An input range mustdefine the primitives `empty`, `popFront`, and `front`. Thefollowing code should compile for any input range. ----R r; // can define a range objectif (r.empty) {} // can test for emptyr.popFront(); // can invoke popFront()auto h = r.front; // can get the front of the range of non-void type---- The following are rules of input ranges are assumed to hold true in allPhobos code. These rules are not checkable at compile-time, so not conformingto these rules when writing ranges or range based code will result inundefined behavior. $(UL $(LI `r.empty` returns `false` if and only if there is more data available in the range.) $(LI `r.empty` evaluated multiple times, without calling `r.popFront`, or otherwise mutating the range object or the underlying data, yields the same result for every evaluation.) $(LI `r.front` returns the current element in the range. It may return by value or by reference.) $(LI `r.front` can be legally evaluated if and only if evaluating `r.empty` has, or would have, equaled `false`.) $(LI `r.front` evaluated multiple times, without calling `r.popFront`, or otherwise mutating the range object or the underlying data, yields the same result for every evaluation.) $(LI `r.popFront` advances to the next element in the range.) $(LI `r.popFront` can be called if and only if evaluating `r.empty` has, or would have, equaled `false`.)) Also, note that Phobos code assumes that the primitives `r.front` and`r.empty` are $(BIGOH 1) time complexity wise or "cheap" in terms ofrunning time. $(BIGOH) statements in the documentation of range functionsare made with this assumption. See_Also: The header of $(MREF std,range) for tutorials on ranges. Params: R = type to be tested E = if present, the elements of the range must be $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to this type Returns: `true` if R is an input range (possibly with element type `E`), `false` if not */enum bool isInputRange(R) = is(typeof(R.init) == R) && is(typeof((R r) { return r.empty; } (R.init)) == bool) && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front))) && !is(typeof((R r) { return r.front; } (R.init)) == void) && is(typeof((R r) => r.popFront)); /// dittoenum bool isInputRange(R, E) = .isInputRange!R && isQualifierConvertible!(ElementType!R, E); ///@safe unittest{ struct A {} struct B { void popFront(); @property bool empty(); @property int front(); } static assert(!isInputRange!A); static assert( isInputRange!B); static assert( isInputRange!(int[])); static assert( isInputRange!(char[])); static assert(!isInputRange!(char[4])); static assert( isInputRange!(inout(int)[])); static assert(!isInputRange!(int[], string)); static assert( isInputRange!(int[], int)); static assert( isInputRange!(int[], const int)); static assert(!isInputRange!(int[], immutable int)); static assert(!isInputRange!(const(int)[], int)); static assert( isInputRange!(const(int)[], const int)); static assert(!isInputRange!(const(int)[], immutable int)); static assert(!isInputRange!(immutable(int)[], int)); static assert( isInputRange!(immutable(int)[], const int)); static assert( isInputRange!(immutable(int)[], immutable int)); static struct NotDefaultConstructible { @disable this(); void popFront(); @property bool empty(); @property int front(); } static assert( isInputRange!NotDefaultConstructible); static struct NotDefaultConstructibleOrCopyable { @disable this(); @disable this(this); void popFront(); @property bool empty(); @property int front(); } static assert(isInputRange!NotDefaultConstructibleOrCopyable); static struct Frontless { void popFront(); @property bool empty(); } static assert(!isInputRange!Frontless); static struct VoidFront { void popFront(); @property bool empty(); void front(); } static assert(!isInputRange!VoidFront);}// https://issues.dlang.org/show_bug.cgi?id=16034@safe unittest{ struct One { int entry = 1; @disable this(this); } assert(isInputRange!(One[]));} @safe unittest{ import std.algorithm.comparison : equal; static struct R { static struct Front { R* impl; @property int value() { return impl._front; } alias value this; } int _front; @property bool empty() { return _front >= 3; } @property auto front() { return Front(&this); } void popFront() { _front++; } } R r; static assert(isInputRange!R); assert(r.equal([ 0, 1, 2 ]));} /+puts the whole raw element `e` into `r`. doPut will not attempt toiterate, slice or transcode `e` in any way shape or form. It will $(B only)call the correct primitive (`r.put(e)`, $(D r.front = e) or`r(e)` once. This can be important when `e` needs to be placed in `r` unchanged.Furthermore, it can be useful when working with `InputRange`s, as doPutguarantees that no more than a single element will be placed.+/private void doPut(R, E)(ref R r, auto ref E e){ static if (is(PointerTarget!R == struct)) enum usingPut = hasMember!(PointerTarget!R, "put"); else enum usingPut = hasMember!(R, "put"); static if (usingPut) { static assert(is(typeof(r.put(e))), "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.put(e); } else static if (isNarrowString!R && is(const(E) == const(typeof(r[0])))) { // one character, we can put it r[0] = e; r = r[1 .. $]; } else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e))) { // slice assign. Note that this is a duplicate from put, but because // putChar uses doPut exclusively, we have to copy it here. immutable len = e.length; r[0 .. len] = e; r = r[len .. $]; } else static if (isInputRange!R) { static assert(is(typeof(r.front = e)), "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.front = e; r.popFront(); } else static if (is(typeof(r(e)))) { r(e); } else { static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); }} @safe unittest{ static assert(!isNativeOutputRange!(int, int)); static assert( isNativeOutputRange!(int[], int)); static assert(!isNativeOutputRange!(int[][], int)); static assert(!isNativeOutputRange!(int, int[])); static assert(!isNativeOutputRange!(int[], int[])); static assert( isNativeOutputRange!(int[][], int[])); static assert(!isNativeOutputRange!(int, int[][])); static assert(!isNativeOutputRange!(int[], int[][])); static assert(!isNativeOutputRange!(int[][], int[][])); static assert(!isNativeOutputRange!(int[4], int)); static assert( isNativeOutputRange!(int[4][], int)); //Scary! static assert( isNativeOutputRange!(int[4][], int[4])); static assert( isNativeOutputRange!( char[], char)); static assert(!isNativeOutputRange!( char[], dchar)); static assert( isNativeOutputRange!(dchar[], char)); static assert( isNativeOutputRange!(dchar[], dchar)); } /++Outputs `e` to `r`. The exact effect is dependent upon the twotypes. Several cases are accepted, as described below. The code snippetsare attempted in order, and the first to compile "wins" and getsevaluated. In this table "doPut" is a method that places `e` into `r`, using thecorrect primitive: `r.put(e)` if `R` defines `put`, $(D r.front = e)if `r` is an input range (followed by `r.popFront()`), or `r(e)`otherwise. $(BOOKTABLE , $(TR $(TH Code Snippet) $(TH Scenario) ) $(TR $(TD `r.doPut(e);`) $(TD `R` specifically accepts an `E`.) ) $(TR $(TD $(D r.doPut([ e ]);)) $(TD `R` specifically accepts an `E[]`.) ) $(TR $(TD `r.putChar(e);`) $(TD `R` accepts some form of string or character. put will transcode the character `e` accordingly.) ) $(TR $(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);)) $(TD Copying range `E` into `R`.) )) Tip: `put` should $(I not) be used "UFCS-style", e.g. `r.put(e)`.Doing this may call `R.put` directly, by-passing any transformationfeature provided by `Range.put`. $(D put(r, e)) is prefered. +/void put(R, E)(ref R r, E e){ //First level: simply straight up put. static if (is(typeof(doPut(r, e)))) { doPut(r, e); } //Optional optimization block for straight up array to array copy. else static if (isDynamicArray!R && !isAutodecodableString!R && isDynamicArray!E && is(typeof(r[] = e[]))) { immutable len = e.length; r[0 .. len] = e[]; r = r[len .. $]; } //Accepts E[] ? else static if (is(typeof(doPut(r, [e]))) && !isDynamicArray!R) { if (__ctfe) { E[1] arr = [e]; doPut(r, arr[]); } else doPut(r, (ref e) @trusted { return (&e)[0 .. 1]; }(e)); } //special case for char to string. else static if (isSomeChar!E && is(typeof(putChar(r, e)))) { putChar(r, e); } //Extract each element from the range //We can use "put" here, so we can recursively test a RoR of E. else static if (isInputRange!E && is(typeof(put(r, e.front)))) { //Special optimization: If E is a narrow string, and r accepts characters no-wider than the string's //Then simply feed the characters 1 by 1. static if (isAutodecodableString!E && !isAggregateType!E && ( (is(E : const char[]) && is(typeof(doPut(r, char.max))) && !is(typeof(doPut(r, dchar.max))) && !is(typeof(doPut(r, wchar.max)))) || (is(E : const wchar[]) && is(typeof(doPut(r, wchar.max))) && !is(typeof(doPut(r, dchar.max)))) ) ) { foreach (c; e) doPut(r, c); } else { for (; !e.empty; e.popFront()) put(r, e.front); } } else { static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); }} /** * When an output range's `put` method only accepts elements of type * `T`, use the global `put` to handle outputting a `T[]` to the range * or vice-versa. */@safe pure unittest{ import std.traits : isSomeChar; static struct A { string data; void put(C)(C c) if (isSomeChar!C) { data ~= c; } } static assert(isOutputRange!(A, char)); auto a = A(); put(a, "Hello"); assert(a.data == "Hello");} /** * `put` treats dynamic arrays as array slices, and will call `popFront` * on the slice after an element has been copied. * * Be sure to save the position of the array before calling `put`. */@safe pure nothrow unittest{ int[] a = [1, 2, 3], b = [10, 20]; auto c = a; put(a, b);Lines 1–500 of 2718. Load more
/** This module is a submodule of $(MREF std, range). It defines the bidirectional and forward range primitives for arrays: $(LREF empty), $(LREF front), $(LREF back), $(LREF popFront), $(LREF popBack) and $(LREF save). It provides basic range functionality by defining several templates for testing whether a given object is a range, and what kind of range it is: $(SCRIPT inhibitQuickIndex = 1;) $(DIVC quickindex, $(BOOKTABLE , $(TR $(TD $(LREF isInputRange)) $(TD Tests if something is an $(I input range), defined to be something from which one can sequentially read data using the primitives `front`, `popFront`, and `empty`. )) $(TR $(TD $(LREF isOutputRange)) $(TD Tests if something is an $(I output range), defined to be something to which one can sequentially write data using the $(LREF put) primitive. )) $(TR $(TD $(LREF isForwardRange)) $(TD Tests if something is a $(I forward range), defined to be an input range with the additional capability that one can save one's current position with the `save` primitive, thus allowing one to iterate over the same range multiple times. )) $(TR $(TD $(LREF isBidirectionalRange)) $(TD Tests if something is a $(I bidirectional range), that is, a forward range that allows reverse traversal using the primitives $(D back) and `popBack`. )) $(TR $(TD $(LREF isRandomAccessRange)) $(TD Tests if something is a $(I random access range), which is a bidirectional range that also supports the array subscripting operation via the primitive `opIndex`. )) )) It also provides number of templates that test for various range capabilities: $(BOOKTABLE , $(TR $(TD $(LREF hasMobileElements)) $(TD Tests if a given range's elements can be moved around using the primitives `moveFront`, `moveBack`, or `moveAt`. )) $(TR $(TD $(LREF ElementType)) $(TD Returns the element type of a given range. )) $(TR $(TD $(LREF ElementEncodingType)) $(TD Returns the encoding element type of a given range. )) $(TR $(TD $(LREF hasSwappableElements)) $(TD Tests if a range is a forward range with swappable elements. )) $(TR $(TD $(LREF hasAssignableElements)) $(TD Tests if a range is a forward range with mutable elements. )) $(TR $(TD $(LREF hasLvalueElements)) $(TD Tests if a range is a forward range with elements that can be passed by reference and have their address taken. )) $(TR $(TD $(LREF hasLength)) $(TD Tests if a given range has the `length` attribute. )) $(TR $(TD $(LREF isInfinite)) $(TD Tests if a given range is an $(I infinite range). )) $(TR $(TD $(LREF hasSlicing)) $(TD Tests if a given range supports the array slicing operation $(D R[x .. y]). )) ) Finally, it includes some convenience functions for manipulating ranges: $(BOOKTABLE , $(TR $(TD $(LREF popFrontN)) $(TD Advances a given range by up to $(I n) elements. )) $(TR $(TD $(LREF popBackN)) $(TD Advances a given bidirectional range from the right by up to $(I n) elements. )) $(TR $(TD $(LREF popFrontExactly)) $(TD Advances a given range by up exactly $(I n) elements. )) $(TR $(TD $(LREF popBackExactly)) $(TD Advances a given bidirectional range from the right by exactly $(I n) elements. )) $(TR $(TD $(LREF moveFront)) $(TD Removes the front element of a range. )) $(TR $(TD $(LREF moveBack)) $(TD Removes the back element of a bidirectional range. )) $(TR $(TD $(LREF moveAt)) $(TD Removes the $(I i)'th element of a random-access range. )) $(TR $(TD $(LREF walkLength)) $(TD Computes the length of any range in O(n) time. )) $(TR $(TD $(LREF put)) $(TD Outputs element `e` to a range. )) ) Source: $(PHOBOSSRC std/range/primitives.d) License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas in building this module goes to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). */ module std.range.primitives; import std.traits; /** Returns `true` if `R` is an input range. An input range must define the primitives `empty`, `popFront`, and `front`. The following code should compile for any input range. ---- R r; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range of non-void type ---- The following are rules of input ranges are assumed to hold true in all Phobos code. These rules are not checkable at compile-time, so not conforming to these rules when writing ranges or range based code will result in undefined behavior. $(UL $(LI `r.empty` returns `false` if and only if there is more data available in the range.) $(LI `r.empty` evaluated multiple times, without calling `r.popFront`, or otherwise mutating the range object or the underlying data, yields the same result for every evaluation.) $(LI `r.front` returns the current element in the range. It may return by value or by reference.) $(LI `r.front` can be legally evaluated if and only if evaluating `r.empty` has, or would have, equaled `false`.) $(LI `r.front` evaluated multiple times, without calling `r.popFront`, or otherwise mutating the range object or the underlying data, yields the same result for every evaluation.) $(LI `r.popFront` advances to the next element in the range.) $(LI `r.popFront` can be called if and only if evaluating `r.empty` has, or would have, equaled `false`.) ) Also, note that Phobos code assumes that the primitives `r.front` and `r.empty` are $(BIGOH 1) time complexity wise or "cheap" in terms of running time. $(BIGOH) statements in the documentation of range functions are made with this assumption. See_Also: The header of $(MREF std,range) for tutorials on ranges. Params: R = type to be tested E = if present, the elements of the range must be $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to this type Returns: `true` if R is an input range (possibly with element type `E`), `false` if not */ enum bool isInputRange(R) = is(typeof(R.init) == R) && is(typeof((R r) { return r.empty; } (R.init)) == bool) && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front))) && !is(typeof((R r) { return r.front; } (R.init)) == void) && is(typeof((R r) => r.popFront)); /// ditto enum bool isInputRange(R, E) = .isInputRange!R && isQualifierConvertible!(ElementType!R, E); /// @safe unittest { struct A {} struct B { void popFront(); @property bool empty(); @property int front(); } static assert(!isInputRange!A); static assert( isInputRange!B); static assert( isInputRange!(int[])); static assert( isInputRange!(char[])); static assert(!isInputRange!(char[4])); static assert( isInputRange!(inout(int)[])); static assert(!isInputRange!(int[], string)); static assert( isInputRange!(int[], int)); static assert( isInputRange!(int[], const int)); static assert(!isInputRange!(int[], immutable int)); static assert(!isInputRange!(const(int)[], int)); static assert( isInputRange!(const(int)[], const int)); static assert(!isInputRange!(const(int)[], immutable int)); static assert(!isInputRange!(immutable(int)[], int)); static assert( isInputRange!(immutable(int)[], const int)); static assert( isInputRange!(immutable(int)[], immutable int)); static struct NotDefaultConstructible { @disable this(); void popFront(); @property bool empty(); @property int front(); } static assert( isInputRange!NotDefaultConstructible); static struct NotDefaultConstructibleOrCopyable { @disable this(); @disable this(this); void popFront(); @property bool empty(); @property int front(); } static assert(isInputRange!NotDefaultConstructibleOrCopyable); static struct Frontless { void popFront(); @property bool empty(); } static assert(!isInputRange!Frontless); static struct VoidFront { void popFront(); @property bool empty(); void front(); } static assert(!isInputRange!VoidFront); } // https://issues.dlang.org/show_bug.cgi?id=16034 @safe unittest { struct One { int entry = 1; @disable this(this); } assert(isInputRange!(One[])); } @safe unittest { import std.algorithm.comparison : equal; static struct R { static struct Front { R* impl; @property int value() { return impl._front; } alias value this; } int _front; @property bool empty() { return _front >= 3; } @property auto front() { return Front(&this); } void popFront() { _front++; } } R r; static assert(isInputRange!R); assert(r.equal([ 0, 1, 2 ])); } /+ puts the whole raw element `e` into `r`. doPut will not attempt to iterate, slice or transcode `e` in any way shape or form. It will $(B only) call the correct primitive (`r.put(e)`, $(D r.front = e) or `r(e)` once. This can be important when `e` needs to be placed in `r` unchanged. Furthermore, it can be useful when working with `InputRange`s, as doPut guarantees that no more than a single element will be placed. +/ private void doPut(R, E)(ref R r, auto ref E e) { static if (is(PointerTarget!R == struct)) enum usingPut = hasMember!(PointerTarget!R, "put"); else enum usingPut = hasMember!(R, "put"); static if (usingPut) { static assert(is(typeof(r.put(e))), "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.put(e); } else static if (isNarrowString!R && is(const(E) == const(typeof(r[0])))) { // one character, we can put it r[0] = e; r = r[1 .. $]; } else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e))) { // slice assign. Note that this is a duplicate from put, but because // putChar uses doPut exclusively, we have to copy it here. immutable len = e.length; r[0 .. len] = e; r = r[len .. $]; } else static if (isInputRange!R) { static assert(is(typeof(r.front = e)), "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.front = e; r.popFront(); } else static if (is(typeof(r(e)))) { r(e); } else { static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } @safe unittest { static assert(!isNativeOutputRange!(int, int)); static assert( isNativeOutputRange!(int[], int)); static assert(!isNativeOutputRange!(int[][], int)); static assert(!isNativeOutputRange!(int, int[])); static assert(!isNativeOutputRange!(int[], int[])); static assert( isNativeOutputRange!(int[][], int[])); static assert(!isNativeOutputRange!(int, int[][])); static assert(!isNativeOutputRange!(int[], int[][])); static assert(!isNativeOutputRange!(int[][], int[][])); static assert(!isNativeOutputRange!(int[4], int)); static assert( isNativeOutputRange!(int[4][], int)); //Scary! static assert( isNativeOutputRange!(int[4][], int[4])); static assert( isNativeOutputRange!( char[], char)); static assert(!isNativeOutputRange!( char[], dchar)); static assert( isNativeOutputRange!(dchar[], char)); static assert( isNativeOutputRange!(dchar[], dchar)); } /++ Outputs `e` to `r`. The exact effect is dependent upon the two types. Several cases are accepted, as described below. The code snippets are attempted in order, and the first to compile "wins" and gets evaluated. In this table "doPut" is a method that places `e` into `r`, using the correct primitive: `r.put(e)` if `R` defines `put`, $(D r.front = e) if `r` is an input range (followed by `r.popFront()`), or `r(e)` otherwise. $(BOOKTABLE , $(TR $(TH Code Snippet) $(TH Scenario) ) $(TR $(TD `r.doPut(e);`) $(TD `R` specifically accepts an `E`.) ) $(TR $(TD $(D r.doPut([ e ]);)) $(TD `R` specifically accepts an `E[]`.) ) $(TR $(TD `r.putChar(e);`) $(TD `R` accepts some form of string or character. put will transcode the character `e` accordingly.) ) $(TR $(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);)) $(TD Copying range `E` into `R`.) ) ) Tip: `put` should $(I not) be used "UFCS-style", e.g. `r.put(e)`. Doing this may call `R.put` directly, by-passing any transformation feature provided by `Range.put`. $(D put(r, e)) is prefered. +/ void put(R, E)(ref R r, E e) { //First level: simply straight up put. static if (is(typeof(doPut(r, e)))) { doPut(r, e); } //Optional optimization block for straight up array to array copy. else static if (isDynamicArray!R && !isAutodecodableString!R && isDynamicArray!E && is(typeof(r[] = e[]))) { immutable len = e.length; r[0 .. len] = e[]; r = r[len .. $]; } //Accepts E[] ? else static if (is(typeof(doPut(r, [e]))) && !isDynamicArray!R) { if (__ctfe) { E[1] arr = [e]; doPut(r, arr[]); } else doPut(r, (ref e) @trusted { return (&e)[0 .. 1]; }(e)); } //special case for char to string. else static if (isSomeChar!E && is(typeof(putChar(r, e)))) { putChar(r, e); } //Extract each element from the range //We can use "put" here, so we can recursively test a RoR of E. else static if (isInputRange!E && is(typeof(put(r, e.front)))) { //Special optimization: If E is a narrow string, and r accepts characters no-wider than the string's //Then simply feed the characters 1 by 1. static if (isAutodecodableString!E && !isAggregateType!E && ( (is(E : const char[]) && is(typeof(doPut(r, char.max))) && !is(typeof(doPut(r, dchar.max))) && !is(typeof(doPut(r, wchar.max)))) || (is(E : const wchar[]) && is(typeof(doPut(r, wchar.max))) && !is(typeof(doPut(r, dchar.max)))) ) ) { foreach (c; e) doPut(r, c); } else { for (; !e.empty; e.popFront()) put(r, e.front); } } else { static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } /** * When an output range's `put` method only accepts elements of type * `T`, use the global `put` to handle outputting a `T[]` to the range * or vice-versa. */ @safe pure unittest { import std.traits : isSomeChar; static struct A { string data; void put(C)(C c) if (isSomeChar!C) { data ~= c; } } static assert(isOutputRange!(A, char)); auto a = A(); put(a, "Hello"); assert(a.data == "Hello"); } /** * `put` treats dynamic arrays as array slices, and will call `popFront` * on the slice after an element has been copied. * * Be sure to save the position of the array before calling `put`. */ @safe pure nothrow unittest { int[] a = [1, 2, 3], b = [10, 20]; auto c = a; put(a, b);