| /** |
| This module is a submodule of $(MREF std, range). |
| |
| 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;) |
| $(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 $(D front), $(D popFront), and $(D 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 $(D 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 $(D 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 $(D 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 $(D moveFront), $(D moveBack), or $(D 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 $(D 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 $(D 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 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 $(D true) if $(D R) is an input range. An input range must |
| define the primitives $(D empty), $(D popFront), and $(D 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. |
| |
| Params: |
| R = type to be tested |
| |
| Returns: |
| true if R is an InputRange, false if not |
| */ |
| enum bool isInputRange(R) = |
| is(typeof(R.init) == R) |
| && is(ReturnType!((R r) => r.empty) == bool) |
| && is(typeof((return ref R r) => r.front)) |
| && !is(ReturnType!((R r) => r.front) == void) |
| && is(typeof((R r) => r.popFront)); |
| |
| /// |
| @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 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); |
| } |
| |
| @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 $(D e) into $(D r). doPut will not attempt to |
| iterate, slice or transcode $(D e) in any way shape or form. It will $(B only) |
| call the correct primitive ($(D r.put(e)), $(D r.front = e) or |
| $(D r(0)) once. |
| |
| This can be important when $(D e) needs to be placed in $(D r) unchanged. |
| Furthermore, it can be useful when working with $(D 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 (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 $(D e) to $(D 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 $(D e) into $(D r), using the |
| correct primitive: $(D r.put(e)) if $(D R) defines $(D put), $(D r.front = e) |
| if $(D r) is an input range (followed by $(D r.popFront())), or $(D r(e)) |
| otherwise. |
| |
| $(BOOKTABLE , |
| $(TR |
| $(TH Code Snippet) |
| $(TH Scenario) |
| ) |
| $(TR |
| $(TD $(D r.doPut(e);)) |
| $(TD $(D R) specifically accepts an $(D E).) |
| ) |
| $(TR |
| $(TD $(D r.doPut([ e ]);)) |
| $(TD $(D R) specifically accepts an $(D E[]).) |
| ) |
| $(TR |
| $(TD $(D r.putChar(e);)) |
| $(TD $(D R) accepts some form of string or character. put will |
| transcode the character $(D e) accordingly.) |
| ) |
| $(TR |
| $(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);)) |
| $(TD Copying range $(D E) into $(D R).) |
| ) |
| ) |
| |
| Tip: $(D put) should $(I not) be used "UFCS-style", e.g. $(D r.put(e)). |
| Doing this may call $(D R.put) directly, by-passing any transformation |
| feature provided by $(D 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 && !isNarrowString!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 (isNarrowString!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 ~ "."); |
| } |
| } |
| |
| @safe pure nothrow @nogc unittest |
| { |
| static struct R() { void put(in char[]) {} } |
| R!() r; |
| put(r, 'a'); |
| } |
| |
| //Helper function to handle chars as quickly and as elegantly as possible |
| //Assumes r.put(e)/r(e) has already been tested |
| private void putChar(R, E)(ref R r, E e) |
| if (isSomeChar!E) |
| { |
| ////@@@9186@@@: Can't use (E[]).init |
| ref const( char)[] cstringInit(); |
| ref const(wchar)[] wstringInit(); |
| ref const(dchar)[] dstringInit(); |
| |
| enum csCond = !isDynamicArray!R && is(typeof(doPut(r, cstringInit()))); |
| enum wsCond = !isDynamicArray!R && is(typeof(doPut(r, wstringInit()))); |
| enum dsCond = !isDynamicArray!R && is(typeof(doPut(r, dstringInit()))); |
| |
| //Use "max" to avoid static type demotion |
| enum ccCond = is(typeof(doPut(r, char.max))); |
| enum wcCond = is(typeof(doPut(r, wchar.max))); |
| //enum dcCond = is(typeof(doPut(r, dchar.max))); |
| |
| //Fast transform a narrow char into a wider string |
| static if ((wsCond && E.sizeof < wchar.sizeof) || (dsCond && E.sizeof < dchar.sizeof)) |
| { |
| enum w = wsCond && E.sizeof < wchar.sizeof; |
| Select!(w, wchar, dchar) c = e; |
| typeof(c)[1] arr = [c]; |
| doPut(r, arr[]); |
| } |
| //Encode a wide char into a narrower string |
| else static if (wsCond || csCond) |
| { |
| import std.utf : encode; |
| /+static+/ Select!(wsCond, wchar[2], char[4]) buf; //static prevents purity. |
| doPut(r, buf[0 .. encode(buf, e)]); |
| } |
| //Slowly encode a wide char into a series of narrower chars |
| else static if (wcCond || ccCond) |
| { |
| import std.encoding : encode; |
| alias C = Select!(wcCond, wchar, char); |
| encode!(C, R)(e, r); |
| } |
| else |
| { |
| static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); |
| } |
| } |
| |
| pure @safe unittest |
| { |
| auto f = delegate (const(char)[]) {}; |
| putChar(f, cast(dchar)'a'); |
| } |
| |
| |
| @safe pure unittest |
| { |
| static struct R() { void put(in char[]) {} } |
| R!() r; |
| putChar(r, 'a'); |
| } |
| |
| @safe unittest |
| { |
| struct A {} |
| static assert(!isInputRange!(A)); |
| struct B |
| { |
| void put(int) {} |
| } |
| B b; |
| put(b, 5); |
| } |
| |
| @safe unittest |
| { |
| int[] a = [1, 2, 3], b = [10, 20]; |
| auto c = a; |
| put(a, b); |
| assert(c == [10, 20, 3]); |
| assert(a == [3]); |
| } |
| |
| @safe unittest |
| { |
| int[] a = new int[10]; |
| int b; |
| static assert(isInputRange!(typeof(a))); |
| put(a, b); |
| } |
| |
| @safe unittest |
| { |
| void myprint(in char[] s) { } |
| auto r = &myprint; |
| put(r, 'a'); |
| } |
| |
| @safe unittest |
| { |
| int[] a = new int[10]; |
| static assert(!__traits(compiles, put(a, 1.0L))); |
| put(a, 1); |
| assert(a.length == 9); |
| /* |
| * a[0] = 65; // OK |
| * a[0] = 'A'; // OK |
| * a[0] = "ABC"[0]; // OK |
| * put(a, "ABC"); // OK |
| */ |
| put(a, "ABC"); |
| assert(a.length == 6); |
| } |
| |
| @safe unittest |
| { |
| char[] a = new char[10]; |
| static assert(!__traits(compiles, put(a, 1.0L))); |
| static assert(!__traits(compiles, put(a, 1))); |
| // char[] is NOT output range. |
| static assert(!__traits(compiles, put(a, 'a'))); |
| static assert(!__traits(compiles, put(a, "ABC"))); |
| } |
| |
| @safe unittest |
| { |
| int[][] a = new int[][10]; |
| int[] b = new int[10]; |
| int c; |
| put(b, c); |
| assert(b.length == 9); |
| put(a, b); |
| assert(a.length == 9); |
| static assert(!__traits(compiles, put(a, c))); |
| } |
| |
| @safe unittest |
| { |
| int[][] a = new int[][](3); |
| int[] b = [1]; |
| auto aa = a; |
| put(aa, b); |
| assert(aa == [[], []]); |
| assert(a == [[1], [], []]); |
| int[][3] c = [2]; |
| aa = a; |
| put(aa, c[]); |
| assert(aa.empty); |
| assert(a == [[2], [2], [2]]); |
| } |
| |
| @safe unittest |
| { |
| // Test fix for bug 7476. |
| struct LockingTextWriter |
| { |
| void put(dchar c){} |
| } |
| struct RetroResult |
| { |
| bool end = false; |
| @property bool empty() const { return end; } |
| @property dchar front(){ return 'a'; } |
| void popFront(){ end = true; } |
| } |
| LockingTextWriter w; |
| RetroResult r; |
| put(w, r); |
| } |
| |
| @system unittest |
| { |
| import std.conv : to; |
| import std.meta : AliasSeq; |
| import std.typecons : tuple; |
| |
| static struct PutC(C) |
| { |
| string result; |
| void put(const(C) c) { result ~= to!string((&c)[0 .. 1]); } |
| } |
| static struct PutS(C) |
| { |
| string result; |
| void put(const(C)[] s) { result ~= to!string(s); } |
| } |
| static struct PutSS(C) |
| { |
| string result; |
| void put(const(C)[][] ss) |
| { |
| foreach (s; ss) |
| result ~= to!string(s); |
| } |
| } |
| |
| PutS!char p; |
| putChar(p, cast(dchar)'a'); |
| |
| //Source Char |
| foreach (SC; AliasSeq!(char, wchar, dchar)) |
| { |
| SC ch = 'I'; |
| dchar dh = '♥'; |
| immutable(SC)[] s = "日本語!"; |
| immutable(SC)[][] ss = ["日本語", "が", "好き", "ですか", "?"]; |
| |
| //Target Char |
| foreach (TC; AliasSeq!(char, wchar, dchar)) |
| { |
| //Testing PutC and PutS |
| foreach (Type; AliasSeq!(PutC!TC, PutS!TC)) |
| (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 |
| Type type; |
| auto sink = new Type(); |
| |
| //Testing put and sink |
| foreach (value ; tuple(type, sink)) |
| { |
| put(value, ch); |
| assert(value.result == "I"); |
| put(value, dh); |
| assert(value.result == "I♥"); |
| put(value, s); |
| assert(value.result == "I♥日本語!"); |
| put(value, ss); |
| assert(value.result == "I♥日本語!日本語が好きですか?"); |
| } |
| }(); |
| } |
| } |
| } |
| |
| @safe unittest |
| { |
| static struct CharRange |
| { |
| char c; |
| enum empty = false; |
| void popFront(){} |
| ref char front() return @property |
| { |
| return c; |
| } |
| } |
| CharRange c; |
| put(c, cast(dchar)'H'); |
| put(c, "hello"d); |
| } |
| |
| @system unittest |
| { |
| // issue 9823 |
| const(char)[] r; |
| void delegate(const(char)[]) dg = (s) { r = s; }; |
| put(dg, ["ABC"]); |
| assert(r == "ABC"); |
| } |
| |
| @safe unittest |
| { |
| // issue 10571 |
| import std.format; |
| string buf; |
| formattedWrite((in char[] s) { buf ~= s; }, "%s", "hello"); |
| assert(buf == "hello"); |
| } |
| |
| @safe unittest |
| { |
| import std.format; |
| import std.meta : AliasSeq; |
| struct PutC(C) |
| { |
| void put(C){} |
| } |
| struct PutS(C) |
| { |
| void put(const(C)[]){} |
| } |
| struct CallC(C) |
| { |
| void opCall(C){} |
| } |
| struct CallS(C) |
| { |
| void opCall(const(C)[]){} |
| } |
| struct FrontC(C) |
| { |
| enum empty = false; |
| auto front()@property{return C.init;} |
| void front(C)@property{} |
| void popFront(){} |
| } |
| struct FrontS(C) |
| { |
| enum empty = false; |
| auto front()@property{return C[].init;} |
| void front(const(C)[])@property{} |
| void popFront(){} |
| } |
| void foo() |
| { |
| foreach (C; AliasSeq!(char, wchar, dchar)) |
| { |
| formattedWrite((C c){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| formattedWrite((const(C)[]){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| formattedWrite(PutC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| formattedWrite(PutS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| CallC!C callC; |
| CallS!C callS; |
| formattedWrite(callC, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| formattedWrite(callS, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| formattedWrite(FrontC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| formattedWrite(FrontS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| } |
| formattedWrite((dchar[]).init, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); |
| } |
| } |
| |
| /+ |
| Returns $(D true) if $(D R) is a native output range for elements of type |
| $(D E). An output range is defined functionally as a range that |
| supports the operation $(D doPut(r, e)) as defined above. if $(D doPut(r, e)) |
| is valid, then $(D put(r,e)) will have the same behavior. |
| |
| The two guarantees isNativeOutputRange gives over the larger $(D isOutputRange) |
| are: |
| 1: $(D e) is $(B exactly) what will be placed (not $(D [e]), for example). |
| 2: if $(D E) is a non $(empty) $(D InputRange), then placing $(D e) is |
| guaranteed to not overflow the range. |
| +/ |
| package(std) enum bool isNativeOutputRange(R, E) = |
| is(typeof(doPut(lvalueOf!R, lvalueOf!E))); |
| |
| @safe unittest |
| { |
| int[] r = new int[](4); |
| static assert(isInputRange!(int[])); |
| static assert( isNativeOutputRange!(int[], int)); |
| static assert(!isNativeOutputRange!(int[], int[])); |
| static assert( isOutputRange!(int[], int[])); |
| |
| if (!r.empty) |
| put(r, 1); //guaranteed to succeed |
| if (!r.empty) |
| put(r, [1, 2]); //May actually error out. |
| } |
| |
| /++ |
| Returns $(D true) if $(D R) is an output range for elements of type |
| $(D E). An output range is defined functionally as a range that |
| supports the operation $(D put(r, e)) as defined above. |
| +/ |
| enum bool isOutputRange(R, E) = |
| is(typeof(put(lvalueOf!R, lvalueOf!E))); |
| |
| /// |
| @safe unittest |
| { |
| void myprint(in char[] s) { } |
| static assert(isOutputRange!(typeof(&myprint), char)); |
| |
| static assert(!isOutputRange!(char[], char)); |
| static assert( isOutputRange!(dchar[], wchar)); |
| static assert( isOutputRange!(dchar[], dchar)); |
| } |
| |
| @safe unittest |
| { |
| import std.array; |
| import std.stdio : writeln; |
| |
| auto app = appender!string(); |
| string s; |
| static assert( isOutputRange!(Appender!string, string)); |
| static assert( isOutputRange!(Appender!string*, string)); |
| static assert(!isOutputRange!(Appender!string, int)); |
| static assert(!isOutputRange!(wchar[], wchar)); |
| static assert( isOutputRange!(dchar[], char)); |
| static assert( isOutputRange!(dchar[], string)); |
| static assert( isOutputRange!(dchar[], wstring)); |
| static assert( isOutputRange!(dchar[], dstring)); |
| |
| static assert(!isOutputRange!(const(int)[], int)); |
| static assert(!isOutputRange!(inout(int)[], int)); |
| } |
| |
| |
| /** |
| Returns $(D true) if $(D R) is a forward range. A forward range is an |
| input range $(D r) that can save "checkpoints" by saving $(D r.save) |
| to another value of type $(D R). Notable examples of input ranges that |
| are $(I not) forward ranges are file/socket ranges; copying such a |
| range will not save the position in the stream, and they most likely |
| reuse an internal buffer as the entire stream does not sit in |
| memory. Subsequently, advancing either the original or the copy will |
| advance the stream, so the copies are not independent. |
| |
| The following code should compile for any forward range. |
| |
| ---- |
| static assert(isInputRange!R); |
| R r1; |
| auto s1 = r1.save; |
| static assert(is(typeof(s1) == R)); |
| ---- |
| |
| Saving a range is not duplicating it; in the example above, $(D r1) |
| and $(D r2) still refer to the same underlying data. They just |
| navigate that data independently. |
| |
| The semantics of a forward range (not checkable during compilation) |
| are the same as for an input range, with the additional requirement |
| that backtracking must be possible by saving a copy of the range |
| object with $(D save) and using it later. |
| */ |
| enum bool isForwardRange(R) = isInputRange!R |
| && is(ReturnType!((R r) => r.save) == R); |
| |
| /// |
| @safe unittest |
| { |
| static assert(!isForwardRange!(int)); |
| static assert( isForwardRange!(int[])); |
| static assert( isForwardRange!(inout(int)[])); |
| } |
| |
| @safe unittest |
| { |
| // BUG 14544 |
| struct R14544 |
| { |
| int front() { return 0;} |
| void popFront() {} |
| bool empty() { return false; } |
| R14544 save() {return this;} |
| } |
| |
| static assert( isForwardRange!R14544 ); |
| } |
| |
| /** |
| Returns $(D true) if $(D R) is a bidirectional range. A bidirectional |
| range is a forward range that also offers the primitives $(D back) and |
| $(D popBack). The following code should compile for any bidirectional |
| range. |
| |
| The semantics of a bidirectional range (not checkable during |
| compilation) are assumed to be the following ($(D r) is an object of |
| type $(D R)): |
| |
| $(UL $(LI $(D r.back) returns (possibly a reference to) the last |
| element in the range. Calling $(D r.back) is allowed only if calling |
| $(D r.empty) has, or would have, returned $(D false).)) |
| */ |
| enum bool isBidirectionalRange(R) = isForwardRange!R |
| && is(typeof((R r) => r.popBack)) |
| && is(ReturnType!((R r) => r.back) == ElementType!R); |
| |
| /// |
| @safe unittest |
| { |
| alias R = int[]; |
| R r = [0,1]; |
| static assert(isForwardRange!R); // is forward range |
| r.popBack(); // can invoke popBack |
| auto t = r.back; // can get the back of the range |
| auto w = r.front; |
| static assert(is(typeof(t) == typeof(w))); // same type for front and back |
| } |
| |
| @safe unittest |
| { |
| struct A {} |
| struct B |
| { |
| void popFront(); |
| @property bool empty(); |
| @property int front(); |
| } |
| struct C |
| { |
| @property bool empty(); |
| @property C save(); |
| void popFront(); |
| @property int front(); |
| void popBack(); |
| @property int back(); |
| } |
| static assert(!isBidirectionalRange!(A)); |
| static assert(!isBidirectionalRange!(B)); |
| static assert( isBidirectionalRange!(C)); |
| static assert( isBidirectionalRange!(int[])); |
| static assert( isBidirectionalRange!(char[])); |
| static assert( isBidirectionalRange!(inout(int)[])); |
| } |
| |
| /** |
| Returns $(D true) if $(D R) is a random-access range. A random-access |
| range is a bidirectional range that also offers the primitive $(D |
| opIndex), OR an infinite forward range that offers $(D opIndex). In |
| either case, the range must either offer $(D length) or be |
| infinite. The following code should compile for any random-access |
| range. |
| |
| The semantics of a random-access range (not checkable during |
| compilation) are assumed to be the following ($(D r) is an object of |
| type $(D R)): $(UL $(LI $(D r.opIndex(n)) returns a reference to the |
| $(D n)th element in the range.)) |
| |
| Although $(D char[]) and $(D wchar[]) (as well as their qualified |
| versions including $(D string) and $(D wstring)) are arrays, $(D |
| isRandomAccessRange) yields $(D false) for them because they use |
| variable-length encodings (UTF-8 and UTF-16 respectively). These types |
| are bidirectional ranges only. |
| */ |
| enum bool isRandomAccessRange(R) = |
| is(typeof(lvalueOf!R[1]) == ElementType!R) |
| && !isNarrowString!R |
| && isForwardRange!R |
| && (isBidirectionalRange!R || isInfinite!R) |
| && (hasLength!R || isInfinite!R) |
| && (isInfinite!R || !is(typeof(lvalueOf!R[$ - 1])) |
| || is(typeof(lvalueOf!R[$ - 1]) == ElementType!R)); |
| |
| /// |
| @safe unittest |
| { |
| import std.traits : isNarrowString; |
| |
| alias R = int[]; |
| |
| // range is finite and bidirectional or infinite and forward. |
| static assert(isBidirectionalRange!R || |
| isForwardRange!R && isInfinite!R); |
| |
| R r = [0,1]; |
| auto e = r[1]; // can index |
| auto f = r.front; |
| static assert(is(typeof(e) == typeof(f))); // same type for indexed and front |
| static assert(!isNarrowString!R); // narrow strings cannot be indexed as ranges |
| static assert(hasLength!R || isInfinite!R); // must have length or be infinite |
| |
| // $ must work as it does with arrays if opIndex works with $ |
| static if (is(typeof(r[$]))) |
| { |
| static assert(is(typeof(f) == typeof(r[$]))); |
| |
| // $ - 1 doesn't make sense with infinite ranges but needs to work |
| // with finite ones. |
| static if (!isInfinite!R) |
| static assert(is(typeof(f) == typeof(r[$ - 1]))); |
| } |
| } |
| |
| @safe unittest |
| { |
| struct A {} |
| struct B |
| { |
| void popFront(); |
| @property bool empty(); |
| @property int front(); |
| } |
| struct C |
| { |
| void popFront(); |
| @property bool empty(); |
| @property int front(); |
| void popBack(); |
| @property int back(); |
| } |
| struct D |
| { |
| @property bool empty(); |
| @property D save(); |
| @property int front(); |
| void popFront(); |
| @property int back(); |
| void popBack(); |
| ref int opIndex(uint); |
| @property size_t length(); |
| alias opDollar = length; |
| //int opSlice(uint, uint); |
| } |
| struct E |
| { |
| bool empty(); |
| E save(); |
| int front(); |
| void popFront(); |
| int back(); |
| void popBack(); |
| ref int opIndex(uint); |
| size_t length(); |
| alias opDollar = length; |
| //int opSlice(uint, uint); |
| } |
| static assert(!isRandomAccessRange!(A)); |
| static assert(!isRandomAccessRange!(B)); |
| static assert(!isRandomAccessRange!(C)); |
| static assert( isRandomAccessRange!(D)); |
| static assert( isRandomAccessRange!(E)); |
| static assert( isRandomAccessRange!(int[])); |
| static assert( isRandomAccessRange!(inout(int)[])); |
| } |
| |
| @safe unittest |
| { |
| // Test fix for bug 6935. |
| struct R |
| { |
| @disable this(); |
| |
| @property bool empty() const { return false; } |
| @property int front() const { return 0; } |
| void popFront() {} |
| |
| @property R save() { return this; } |
| |
| @property int back() const { return 0; } |
| void popBack(){} |
| |
| int opIndex(size_t n) const { return 0; } |
| @property size_t length() const { return 0; } |
| alias opDollar = length; |
| |
| void put(int e){ } |
| } |
| static assert(isInputRange!R); |
| static assert(isForwardRange!R); |
| static assert(isBidirectionalRange!R); |
| static assert(isRandomAccessRange!R); |
| static assert(isOutputRange!(R, int)); |
| } |
| |
| /** |
| Returns $(D true) iff $(D R) is an input range that supports the |
| $(D moveFront) primitive, as well as $(D moveBack) and $(D moveAt) if it's a |
| bidirectional or random access range. These may be explicitly implemented, or |
| may work via the default behavior of the module level functions $(D moveFront) |
| and friends. The following code should compile for any range |
| with mobile elements. |
| |
| ---- |
| alias E = ElementType!R; |
| R r; |
| static assert(isInputRange!R); |
| static assert(is(typeof(moveFront(r)) == E)); |
| static if (isBidirectionalRange!R) |
| static assert(is(typeof(moveBack(r)) == E)); |
| static if (isRandomAccessRange!R) |
| static assert(is(typeof(moveAt(r, 0)) == E)); |
| ---- |
| */ |
| enum bool hasMobileElements(R) = |
| isInputRange!R |
| && is(typeof(moveFront(lvalueOf!R)) == ElementType!R) |
| && (!isBidirectionalRange!R |
| || is(typeof(moveBack(lvalueOf!R)) == ElementType!R)) |
| && (!isRandomAccessRange!R |
| || is(typeof(moveAt(lvalueOf!R, 0)) == ElementType!R)); |
| |
| /// |
| @safe unittest |
| { |
| import std.algorithm.iteration : map; |
| import std.range : iota, repeat; |
| |
| static struct HasPostblit |
| { |
| this(this) {} |
| } |
| |
| auto nonMobile = map!"a"(repeat(HasPostblit.init)); |
| static assert(!hasMobileElements!(typeof(nonMobile))); |
| static assert( hasMobileElements!(int[])); |
| static assert( hasMobileElements!(inout(int)[])); |
| static assert( hasMobileElements!(typeof(iota(1000)))); |
| |
| static assert( hasMobileElements!( string)); |
| static assert( hasMobileElements!(dstring)); |
| static assert( hasMobileElements!( char[])); |
| static assert( hasMobileElements!(dchar[])); |
| } |
| |
| /** |
| The element type of $(D R). $(D R) does not have to be a range. The |
| element type is determined as the type yielded by $(D r.front) for an |
| object $(D r) of type $(D R). For example, $(D ElementType!(T[])) is |
| $(D T) if $(D T[]) isn't a narrow string; if it is, the element type is |
| $(D dchar). If $(D R) doesn't have $(D front), $(D ElementType!R) is |
| $(D void). |
| */ |
| template ElementType(R) |
| { |
| static if (is(typeof(R.init.front.init) T)) |
| alias ElementType = T; |
| else |
| alias ElementType = void; |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.range : iota; |
| |
| // Standard arrays: returns the type of the elements of the array |
| static assert(is(ElementType!(int[]) == int)); |
| |
| // Accessing .front retrieves the decoded dchar |
| static assert(is(ElementType!(char[]) == dchar)); // rvalue |
| static assert(is(ElementType!(dchar[]) == dchar)); // lvalue |
| |
| // Ditto |
| static assert(is(ElementType!(string) == dchar)); |
| static assert(is(ElementType!(dstring) == immutable(dchar))); |
| |
| // For ranges it gets the type of .front. |
| auto range = iota(0, 10); |
| static assert(is(ElementType!(typeof(range)) == int)); |
| } |
| |
| @safe unittest |
| { |
| static assert(is(ElementType!(byte[]) == byte)); |
| static assert(is(ElementType!(wchar[]) == dchar)); // rvalue |
| static assert(is(ElementType!(wstring) == dchar)); |
| } |
| |
| @safe unittest |
| { |
| enum XYZ : string { a = "foo" } |
| auto x = XYZ.a.front; |
| immutable char[3] a = "abc"; |
| int[] i; |
| void[] buf; |
| static assert(is(ElementType!(XYZ) == dchar)); |
| static assert(is(ElementType!(typeof(a)) == dchar)); |
| static assert(is(ElementType!(typeof(i)) == int)); |
| static assert(is(ElementType!(typeof(buf)) == void)); |
| static assert(is(ElementType!(inout(int)[]) == inout(int))); |
| static assert(is(ElementType!(inout(int[])) == inout(int))); |
| } |
| |
| @safe unittest |
| { |
| static assert(is(ElementType!(int[5]) == int)); |
| static assert(is(ElementType!(int[0]) == int)); |
| static assert(is(ElementType!(char[5]) == dchar)); |
| static assert(is(ElementType!(char[0]) == dchar)); |
| } |
| |
| @safe unittest //11336 |
| { |
| static struct S |
| { |
| this(this) @disable; |
| } |
| static assert(is(ElementType!(S[]) == S)); |
| } |
| |
| @safe unittest // 11401 |
| { |
| // ElementType should also work for non-@propety 'front' |
| struct E { ushort id; } |
| struct R |
| { |
| E front() { return E.init; } |
| } |
| static assert(is(ElementType!R == E)); |
| } |
| |
| /** |
| The encoding element type of $(D R). For narrow strings ($(D char[]), |
| $(D wchar[]) and their qualified variants including $(D string) and |
| $(D wstring)), $(D ElementEncodingType) is the character type of the |
| string. For all other types, $(D ElementEncodingType) is the same as |
| $(D ElementType). |
| */ |
| template ElementEncodingType(R) |
| { |
| static if (is(StringTypeOf!R) && is(R : E[], E)) |
| alias ElementEncodingType = E; |
| else |
| alias ElementEncodingType = ElementType!R; |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.range : iota; |
| // internally the range stores the encoded type |
| static assert(is(ElementEncodingType!(char[]) == char)); |
| |
| static assert(is(ElementEncodingType!(wstring) == immutable(wchar))); |
| |
| static assert(is(ElementEncodingType!(byte[]) == byte)); |
| |
| auto range = iota(0, 10); |
| static assert(is(ElementEncodingType!(typeof(range)) == int)); |
| } |
| |
| @safe unittest |
| { |
| static assert(is(ElementEncodingType!(wchar[]) == wchar)); |
| static assert(is(ElementEncodingType!(dchar[]) == dchar)); |
| static assert(is(ElementEncodingType!(string) == immutable(char))); |
| static assert(is(ElementEncodingType!(dstring) == immutable(dchar))); |
| static assert(is(ElementEncodingType!(int[]) == int)); |
| } |
| |
| @safe unittest |
| { |
| enum XYZ : string { a = "foo" } |
| auto x = XYZ.a.front; |
| immutable char[3] a = "abc"; |
| int[] i; |
| void[] buf; |
| static assert(is(ElementType!(XYZ) : dchar)); |
| static assert(is(ElementEncodingType!(char[]) == char)); |
| static assert(is(ElementEncodingType!(string) == immutable char)); |
| static assert(is(ElementType!(typeof(a)) : dchar)); |
| static assert(is(ElementType!(typeof(i)) == int)); |
| static assert(is(ElementEncodingType!(typeof(i)) == int)); |
| static assert(is(ElementType!(typeof(buf)) : void)); |
| |
| static assert(is(ElementEncodingType!(inout char[]) : inout(char))); |
| } |
| |
| @safe unittest |
| { |
| static assert(is(ElementEncodingType!(int[5]) == int)); |
| static assert(is(ElementEncodingType!(int[0]) == int)); |
| static assert(is(ElementEncodingType!(char[5]) == char)); |
| static assert(is(ElementEncodingType!(char[0]) == char)); |
| } |
| |
| /** |
| Returns $(D true) if $(D R) is an input range and has swappable |
| elements. The following code should compile for any range |
| with swappable elements. |
| |
| ---- |
| R r; |
| static assert(isInputRange!R); |
| swap(r.front, r.front); |
| static if (isBidirectionalRange!R) swap(r.back, r.front); |
| static if (isRandomAccessRange!R) swap(r[0], r.front); |
| ---- |
| */ |
| template hasSwappableElements(R) |
| { |
| import std.algorithm.mutation : swap; |
| enum bool hasSwappableElements = isInputRange!R |
| && is(typeof((ref R r) => swap(r.front, r.front))) |
| && (!isBidirectionalRange!R |
| || is(typeof((ref R r) => swap(r.back, r.front)))) |
| && (!isRandomAccessRange!R |
| || is(typeof((ref R r) => swap(r[0], r.front)))); |
| } |
| |
| /// |
| @safe unittest |
| { |
| static assert(!hasSwappableElements!(const int[])); |
| static assert(!hasSwappableElements!(const(int)[])); |
| static assert(!hasSwappableElements!(inout(int)[])); |
| static assert( hasSwappableElements!(int[])); |
| |
| static assert(!hasSwappableElements!( string)); |
| static assert(!hasSwappableElements!(dstring)); |
| static assert(!hasSwappableElements!( char[])); |
| static assert( hasSwappableElements!(dchar[])); |
| } |
| |
| /** |
| Returns $(D true) if $(D R) is an input range and has mutable |
| elements. The following code should compile for any range |
| with assignable elements. |
| |
| ---- |
| R r; |
| static assert(isInputRange!R); |
| r.front = r.front; |
| static if (isBidirectionalRange!R) r.back = r.front; |
| static if (isRandomAccessRange!R) r[0] = r.front; |
| ---- |
| */ |
| enum bool hasAssignableElements(R) = isInputRange!R |
| && is(typeof(lvalueOf!R.front = lvalueOf!R.front)) |
| && (!isBidirectionalRange!R |
| || is(typeof(lvalueOf!R.back = lvalueOf!R.back))) |
| && (!isRandomAccessRange!R |
| || is(typeof(lvalueOf!R[0] = lvalueOf!R.front))); |
| |
| /// |
| @safe unittest |
| { |
| static assert(!hasAssignableElements!(const int[])); |
| static assert(!hasAssignableElements!(const(int)[])); |
| static assert( hasAssignableElements!(int[])); |
| static assert(!hasAssignableElements!(inout(int)[])); |
| |
| static assert(!hasAssignableElements!( string)); |
| static assert(!hasAssignableElements!(dstring)); |
| static assert(!hasAssignableElements!( char[])); |
| static assert( hasAssignableElements!(dchar[])); |
| } |
| |
| /** |
| Tests whether the range $(D R) has lvalue elements. These are defined as |
| elements that can be passed by reference and have their address taken. |
| The following code should compile for any range with lvalue elements. |
| ---- |
| void passByRef(ref ElementType!R stuff); |
| ... |
| static assert(isInputRange!R); |
| passByRef(r.front); |
| static if (isBidirectionalRange!R) passByRef(r.back); |
| static if (isRandomAccessRange!R) passByRef(r[0]); |
| ---- |
| */ |
| enum bool hasLvalueElements(R) = isInputRange!R |
| && is(typeof(((ref x) => x)(lvalueOf!R.front))) |
| && (!isBidirectionalRange!R |
| || is(typeof(((ref x) => x)(lvalueOf!R.back)))) |
| && (!isRandomAccessRange!R |
| || is(typeof(((ref x) => x)(lvalueOf!R[0])))); |
| |
| /// |
| @safe unittest |
| { |
| import std.range : iota, chain; |
| |
| static assert( hasLvalueElements!(int[])); |
| static assert( hasLvalueElements!(const(int)[])); |
| static assert( hasLvalueElements!(inout(int)[])); |
| static assert( hasLvalueElements!(immutable(int)[])); |
| static assert(!hasLvalueElements!(typeof(iota(3)))); |
| |
| static assert(!hasLvalueElements!( string)); |
| static assert( hasLvalueElements!(dstring)); |
| static assert(!hasLvalueElements!( char[])); |
| static assert( hasLvalueElements!(dchar[])); |
| |
| auto c = chain([1, 2, 3], [4, 5, 6]); |
| static assert( hasLvalueElements!(typeof(c))); |
| } |
| |
| @safe unittest |
| { |
| // bugfix 6336 |
| struct S { immutable int value; } |
| static assert( isInputRange!(S[])); |
| static assert( hasLvalueElements!(S[])); |
| } |
| |
| /** |
| Yields `true` if `R` has a `length` member that returns a value of `size_t` |
| type. `R` does not have to be a range. If `R` is a range, algorithms in the |
| standard library are only guaranteed to support `length` with type `size_t`. |
| |
| Note that `length` is an optional primitive as no range must implement it. Some |
| ranges do not store their length explicitly, some cannot compute it without |
| actually exhausting the range (e.g. socket streams), and some other ranges may |
| be infinite. |
| |
| Although narrow string types (`char[]`, `wchar[]`, and their qualified |
| derivatives) do define a `length` property, `hasLength` yields `false` for them. |
| This is because a narrow string's length does not reflect the number of |
| characters, but instead the number of encoding units, and as such is not useful |
| with range-oriented algorithms. To use strings as random-access ranges with |
| length, use $(REF representation, std, string) or $(REF byCodeUnit, std, utf). |
| */ |
| template hasLength(R) |
| { |
| static if (is(typeof(((R* r) => r.length)(null)) Length)) |
| enum bool hasLength = is(Length == size_t) && !isNarrowString!R; |
| else |
| enum bool hasLength = false; |
| } |
| |
| /// |
| @safe unittest |
| { |
| static assert(!hasLength!(char[])); |
| static assert( hasLength!(int[])); |
| static assert( hasLength!(inout(int)[])); |
| |
| struct A { size_t length() { return 0; } } |
| struct B { @property size_t length() { return 0; } } |
| static assert( hasLength!(A)); |
| static assert( hasLength!(B)); |
| } |
| |
| // test combinations which are invalid on some platforms |
| unittest |
| { |
| struct A { ulong length; } |
| struct B { @property uint length() { return 0; } } |
| |
| static if (is(size_t == uint)) |
| { |
| static assert(!hasLength!(A)); |
| static assert(hasLength!(B)); |
| } |
| else static if (is(size_t == ulong)) |
| { |
| static assert(hasLength!(A)); |
| static assert(!hasLength!(B)); |
| } |
| } |
| |
| // test combinations which are invalid on all platforms |
| unittest |
| { |
| struct A { long length; } |
| struct B { int length; } |
| struct C { ubyte length; } |
| struct D { char length; } |
| static assert(!hasLength!(A)); |
| static assert(!hasLength!(B)); |
| static assert(!hasLength!(C)); |
| static assert(!hasLength!(D)); |
| } |
| |
| /** |
| Returns $(D true) if $(D R) is an infinite input range. An |
| infinite input range is an input range that has a statically-defined |
| enumerated member called $(D empty) that is always $(D false), |
| for example: |
| |
| ---- |
| struct MyInfiniteRange |
| { |
| enum bool empty = false; |
| ... |
| } |
| ---- |
| */ |
| |
| template isInfinite(R) |
| { |
| static if (isInputRange!R && __traits(compiles, { enum e = R.empty; })) |
| enum bool isInfinite = !R.empty; |
| else |
| enum bool isInfinite = false; |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.range : Repeat; |
| static assert(!isInfinite!(int[])); |
| static assert( isInfinite!(Repeat!(int))); |
| } |
| |
| /** |
| Returns $(D true) if $(D R) offers a slicing operator with integral boundaries |
| that returns a forward range type. |
| |
| For finite ranges, the result of $(D opSlice) must be of the same type as the |
| original range type. If the range defines $(D opDollar), then it must support |
| subtraction. |
| |
| For infinite ranges, when $(I not) using $(D opDollar), the result of |
| $(D opSlice) must be the result of $(LREF take) or $(LREF takeExactly) on the |
| original range (they both return the same type for infinite ranges). However, |
| when using $(D opDollar), the result of $(D opSlice) must be that of the |
| original range type. |
| |
| The following expression must be true for `hasSlicing` to be `true`: |
| |
| ---- |
| isForwardRange!R |
| && !isNarrowString!R |
| && is(ReturnType!((R r) => r[1 .. 1].length) == size_t) |
| && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R) |
| && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R)) |
| && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R |
| || is(typeof(lvalueOf!R[0 .. $ - 1]) == R)) |
| && is(typeof((ref R r) |
| { |
| static assert(isForwardRange!(typeof(r[1 .. 2]))); |
| })); |
| ---- |
| */ |
| enum bool hasSlicing(R) = isForwardRange!R |
| && !isNarrowString!R |
| && is(ReturnType!((R r) => r[1 .. 1].length) == size_t) |
| && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R) |
| && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R)) |
| && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R |
| || is(typeof(lvalueOf!R[0 .. $ - 1]) == R)) |
| && is(typeof((ref R r) |
| { |
| static assert(isForwardRange!(typeof(r[1 .. 2]))); |
| })); |
| |
| /// |
| @safe unittest |
| { |
| import std.range : takeExactly; |
| static assert( hasSlicing!(int[])); |
| static assert( hasSlicing!(const(int)[])); |
| static assert(!hasSlicing!(const int[])); |
| static assert( hasSlicing!(inout(int)[])); |
| static assert(!hasSlicing!(inout int [])); |
| static assert( hasSlicing!(immutable(int)[])); |
| static assert(!hasSlicing!(immutable int[])); |
| static assert(!hasSlicing!string); |
| static assert( hasSlicing!dstring); |
| |
| enum rangeFuncs = "@property int front();" ~ |
| "void popFront();" ~ |
| "@property bool empty();" ~ |
| "@property auto save() { return this; }" ~ |
| "@property size_t length();"; |
| |
| struct A { mixin(rangeFuncs); int opSlice(size_t, size_t); } |
| struct B { mixin(rangeFuncs); B opSlice(size_t, size_t); } |
| struct C { mixin(rangeFuncs); @disable this(); C opSlice(size_t, size_t); } |
| struct D { mixin(rangeFuncs); int[] opSlice(size_t, size_t); } |
| static assert(!hasSlicing!(A)); |
| static assert( hasSlicing!(B)); |
| static assert( hasSlicing!(C)); |
| static assert(!hasSlicing!(D)); |
| |
| struct InfOnes |
| { |
| enum empty = false; |
| void popFront() {} |
| @property int front() { return 1; } |
| @property InfOnes save() { return this; } |
| auto opSlice(size_t i, size_t j) { return takeExactly(this, j - i); } |
| auto opSlice(size_t i, Dollar d) { return this; } |
| |
| struct Dollar {} |
| Dollar opDollar() const { return Dollar.init; } |
| } |
| |
| static assert(hasSlicing!InfOnes); |
| } |
| |
| /** |
| This is a best-effort implementation of $(D length) for any kind of |
| range. |
| |
| If $(D hasLength!Range), simply returns $(D range.length) without |
| checking $(D upTo) (when specified). |
| |
| Otherwise, walks the range through its length and returns the number |
| of elements seen. Performes $(BIGOH n) evaluations of $(D range.empty) |
| and $(D range.popFront()), where $(D n) is the effective length of $(D |
| range). |
| |
| The $(D upTo) parameter is useful to "cut the losses" in case |
| the interest is in seeing whether the range has at least some number |
| of elements. If the parameter $(D upTo) is specified, stops if $(D |
| upTo) steps have been taken and returns $(D upTo). |
| |
| Infinite ranges are compatible, provided the parameter $(D upTo) is |
| specified, in which case the implementation simply returns upTo. |
| */ |
| auto walkLength(Range)(Range range) |
| if (isInputRange!Range && !isInfinite!Range) |
| { |
| static if (hasLength!Range) |
| return range.length; |
| else |
| { |
| size_t result; |
| for ( ; !range.empty ; range.popFront() ) |
| ++result; |
| return result; |
| } |
| } |
| /// ditto |
| auto walkLength(Range)(Range range, const size_t upTo) |
| if (isInputRange!Range) |
| { |
| static if (hasLength!Range) |
| return range.length; |
| else static if (isInfinite!Range) |
| return upTo; |
| else |
| { |
| size_t result; |
| for ( ; result < upTo && !range.empty ; range.popFront() ) |
| ++result; |
| return result; |
| } |
| } |
| |
| @safe unittest |
| { |
| import std.algorithm.iteration : filter; |
| import std.range : recurrence, take; |
| |
| //hasLength Range |
| int[] a = [ 1, 2, 3 ]; |
| assert(walkLength(a) == 3); |
| assert(walkLength(a, 0) == 3); |
| assert(walkLength(a, 2) == 3); |
| assert(walkLength(a, 4) == 3); |
| |
| //Forward Range |
| auto b = filter!"true"([1, 2, 3, 4]); |
| assert(b.walkLength() == 4); |
| assert(b.walkLength(0) == 0); |
| assert(b.walkLength(2) == 2); |
| assert(b.walkLength(4) == 4); |
| assert(b.walkLength(6) == 4); |
| |
| //Infinite Range |
| auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); |
| assert(!__traits(compiles, fibs.walkLength())); |
| assert(fibs.take(10).walkLength() == 10); |
| assert(fibs.walkLength(55) == 55); |
| } |
| |
| /** |
| Eagerly advances $(D r) itself (not a copy) up to $(D n) times (by |
| calling $(D r.popFront)). $(D popFrontN) takes $(D r) by $(D ref), |
| so it mutates the original range. Completes in $(BIGOH 1) steps for ranges |
| that support slicing and have length. |
| Completes in $(BIGOH n) time for all other ranges. |
| |
| Returns: |
| How much $(D r) was actually advanced, which may be less than $(D n) if |
| $(D r) did not have at least $(D n) elements. |
| |
| $(D popBackN) will behave the same but instead removes elements from |
| the back of the (bidirectional) range instead of the front. |
| |
| See_Also: $(REF drop, std, range), $(REF dropBack, std, range) |
| */ |
| size_t popFrontN(Range)(ref Range r, size_t n) |
| if (isInputRange!Range) |
| { |
| static if (hasLength!Range) |
| { |
| n = cast(size_t) (n < r.length ? n : r.length); |
| } |
| |
| static if (hasSlicing!Range && is(typeof(r = r[n .. $]))) |
| { |
| r = r[n .. $]; |
| } |
| else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. |
| { |
| r = r[n .. r.length]; |
| } |
| else |
| { |
| static if (hasLength!Range) |
| { |
| foreach (i; 0 .. n) |
| r.popFront(); |
| } |
| else |
| { |
| foreach (i; 0 .. n) |
| { |
| if (r.empty) return i; |
| r.popFront(); |
| } |
| } |
| } |
| return n; |
| } |
| |
| /// ditto |
| size_t popBackN(Range)(ref Range r, size_t n) |
| if (isBidirectionalRange!Range) |
| { |
| static if (hasLength!Range) |
| { |
| n = cast(size_t) (n < r.length ? n : r.length); |
| } |
| |
| static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n]))) |
| { |
| r = r[0 .. $ - n]; |
| } |
| else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. |
| { |
| r = r[0 .. r.length - n]; |
| } |
| else |
| { |
| static if (hasLength!Range) |
| { |
| foreach (i; 0 .. n) |
| r.popBack(); |
| } |
| else |
| { |
| foreach (i; 0 .. n) |
| { |
| if (r.empty) return i; |
| r.popBack(); |
| } |
| } |
| } |
| return n; |
| } |
| |
| /// |
| @safe unittest |
| { |
| int[] a = [ 1, 2, 3, 4, 5 ]; |
| a.popFrontN(2); |
| assert(a == [ 3, 4, 5 ]); |
| a.popFrontN(7); |
| assert(a == [ ]); |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.algorithm.comparison : equal; |
| import std.range : iota; |
| auto LL = iota(1L, 7L); |
| auto r = popFrontN(LL, 2); |
| assert(equal(LL, [3L, 4L, 5L, 6L])); |
| assert(r == 2); |
| } |
| |
| /// |
| @safe unittest |
| { |
| int[] a = [ 1, 2, 3, 4, 5 ]; |
| a.popBackN(2); |
| assert(a == [ 1, 2, 3 ]); |
| a.popBackN(7); |
| assert(a == [ ]); |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.algorithm.comparison : equal; |
| import std.range : iota; |
| auto LL = iota(1L, 7L); |
| auto r = popBackN(LL, 2); |
| assert(equal(LL, [1L, 2L, 3L, 4L])); |
| assert(r == 2); |
| } |
| |
| /** |
| Eagerly advances $(D r) itself (not a copy) exactly $(D n) times (by |
| calling $(D r.popFront)). $(D popFrontExactly) takes $(D r) by $(D ref), |
| so it mutates the original range. Completes in $(BIGOH 1) steps for ranges |
| that support slicing, and have either length or are infinite. |
| Completes in $(BIGOH n) time for all other ranges. |
| |
| Note: Unlike $(LREF popFrontN), $(D popFrontExactly) will assume that the |
| range holds at least $(D n) elements. This makes $(D popFrontExactly) |
| faster than $(D popFrontN), but it also means that if $(D range) does |
| not contain at least $(D n) elements, it will attempt to call $(D popFront) |
| on an empty range, which is undefined behavior. So, only use |
| $(D popFrontExactly) when it is guaranteed that $(D range) holds at least |
| $(D n) elements. |
| |
| $(D popBackExactly) will behave the same but instead removes elements from |
| the back of the (bidirectional) range instead of the front. |
| |
| See_Also: $(REF dropExcatly, std, range), $(REF dropBackExactly, std, range) |
| */ |
| void popFrontExactly(Range)(ref Range r, size_t n) |
| if (isInputRange!Range) |
| { |
| static if (hasLength!Range) |
| assert(n <= r.length, "range is smaller than amount of items to pop"); |
| |
| static if (hasSlicing!Range && is(typeof(r = r[n .. $]))) |
| r = r[n .. $]; |
| else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. |
| r = r[n .. r.length]; |
| else |
| foreach (i; 0 .. n) |
| r.popFront(); |
| } |
| |
| /// ditto |
| void popBackExactly(Range)(ref Range r, size_t n) |
| if (isBidirectionalRange!Range) |
| { |
| static if (hasLength!Range) |
| assert(n <= r.length, "range is smaller than amount of items to pop"); |
| |
| static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n]))) |
| r = r[0 .. $ - n]; |
| else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. |
| r = r[0 .. r.length - n]; |
| else |
| foreach (i; 0 .. n) |
| r.popBack(); |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.algorithm.comparison : equal; |
| import std.algorithm.iteration : filterBidirectional; |
| |
| auto a = [1, 2, 3]; |
| a.popFrontExactly(1); |
| assert(a == [2, 3]); |
| a.popBackExactly(1); |
| assert(a == [2]); |
| |
| string s = "日本語"; |
| s.popFrontExactly(1); |
| assert(s == "本語"); |
| s.popBackExactly(1); |
| assert(s == "本"); |
| |
| auto bd = filterBidirectional!"true"([1, 2, 3]); |
| bd.popFrontExactly(1); |
| assert(bd.equal([2, 3])); |
| bd.popBackExactly(1); |
| assert(bd.equal([2])); |
| } |
| |
| /** |
| Moves the front of $(D r) out and returns it. Leaves $(D r.front) in a |
| destroyable state that does not allocate any resources (usually equal |
| to its $(D .init) value). |
| */ |
| ElementType!R moveFront(R)(R r) |
| { |
| static if (is(typeof(&r.moveFront))) |
| { |
| return r.moveFront(); |
| } |
| else static if (!hasElaborateCopyConstructor!(ElementType!R)) |
| { |
| return r.front; |
| } |
| else static if (is(typeof(&(r.front())) == ElementType!R*)) |
| { |
| import std.algorithm.mutation : move; |
| return move(r.front); |
| } |
| else |
| { |
| static assert(0, |
| "Cannot move front of a range with a postblit and an rvalue front."); |
| } |
| } |
| |
| /// |
| @safe unittest |
| { |
| auto a = [ 1, 2, 3 ]; |
| assert(moveFront(a) == 1); |
| assert(a.length == 3); |
| |
| // define a perfunctory input range |
| struct InputRange |
| { |
| enum bool empty = false; |
| enum int front = 7; |
| void popFront() {} |
| int moveFront() { return 43; } |
| } |
| InputRange r; |
| // calls r.moveFront |
| assert(moveFront(r) == 43); |
| } |
| |
| @safe unittest |
| { |
| struct R |
| { |
| @property ref int front() { static int x = 42; return x; } |
| this(this){} |
| } |
| R r; |
| assert(moveFront(r) == 42); |
| } |
| |
| /** |
| Moves the back of $(D r) out and returns it. Leaves $(D r.back) in a |
| destroyable state that does not allocate any resources (usually equal |
| to its $(D .init) value). |
| */ |
| ElementType!R moveBack(R)(R r) |
| { |
| static if (is(typeof(&r.moveBack))) |
| { |
| return r.moveBack(); |
| } |
| else static if (!hasElaborateCopyConstructor!(ElementType!R)) |
| { |
| return r.back; |
| } |
| else static if (is(typeof(&(r.back())) == ElementType!R*)) |
| { |
| import std.algorithm.mutation : move; |
| return move(r.back); |
| } |
| else |
| { |
| static assert(0, |
| "Cannot move back of a range with a postblit and an rvalue back."); |
| } |
| } |
| |
| /// |
| @safe unittest |
| { |
| struct TestRange |
| { |
| int payload = 5; |
| @property bool empty() { return false; } |
| @property TestRange save() { return this; } |
| @property ref int front() return { return payload; } |
| @property ref int back() return { return payload; } |
| void popFront() { } |
| void popBack() { } |
| } |
| static assert(isBidirectionalRange!TestRange); |
| TestRange r; |
| auto x = moveBack(r); |
| assert(x == 5); |
| } |
| |
| /** |
| Moves element at index $(D i) of $(D r) out and returns it. Leaves $(D |
| r[i]) in a destroyable state that does not allocate any resources |
| (usually equal to its $(D .init) value). |
| */ |
| ElementType!R moveAt(R)(R r, size_t i) |
| { |
| static if (is(typeof(&r.moveAt))) |
| { |
| return r.moveAt(i); |
| } |
| else static if (!hasElaborateCopyConstructor!(ElementType!(R))) |
| { |
| return r[i]; |
| } |
| else static if (is(typeof(&r[i]) == ElementType!R*)) |
| { |
| import std.algorithm.mutation : move; |
| return move(r[i]); |
| } |
| else |
| { |
| static assert(0, |
| "Cannot move element of a range with a postblit and rvalue elements."); |
| } |
| } |
| |
| /// |
| @safe unittest |
| { |
| auto a = [1,2,3,4]; |
| foreach (idx, it; a) |
| { |
| assert(it == moveAt(a, idx)); |
| } |
| } |
| |
| @safe unittest |
| { |
| import std.internal.test.dummyrange; |
| |
| foreach (DummyType; AllDummyRanges) |
| { |
| auto d = DummyType.init; |
| assert(moveFront(d) == 1); |
| |
| static if (isBidirectionalRange!DummyType) |
| { |
| assert(moveBack(d) == 10); |
| } |
| |
| static if (isRandomAccessRange!DummyType) |
| { |
| assert(moveAt(d, 2) == 3); |
| } |
| } |
| } |
| |
| /** |
| Implements the range interface primitive $(D empty) for built-in |
| arrays. Due to the fact that nonmember functions can be called with |
| the first argument using the dot notation, $(D array.empty) is |
| equivalent to $(D empty(array)). |
| */ |
| @property bool empty(T)(in T[] a) @safe pure nothrow @nogc |
| { |
| return !a.length; |
| } |
| |
| /// |
| @safe pure nothrow unittest |
| { |
| auto a = [ 1, 2, 3 ]; |
| assert(!a.empty); |
| assert(a[3 .. $].empty); |
| } |
| |
| /** |
| Implements the range interface primitive $(D save) for built-in |
| arrays. Due to the fact that nonmember functions can be called with |
| the first argument using the dot notation, $(D array.save) is |
| equivalent to $(D save(array)). The function does not duplicate the |
| content of the array, it simply returns its argument. |
| */ |
| @property T[] save(T)(T[] a) @safe pure nothrow @nogc |
| { |
| return a; |
| } |
| |
| /// |
| @safe pure nothrow unittest |
| { |
| auto a = [ 1, 2, 3 ]; |
| auto b = a.save; |
| assert(b is a); |
| } |
| |
| /** |
| Implements the range interface primitive $(D popFront) for built-in |
| arrays. Due to the fact that nonmember functions can be called with |
| the first argument using the dot notation, $(D array.popFront) is |
| equivalent to $(D popFront(array)). For $(GLOSSARY narrow strings), |
| $(D popFront) automatically advances to the next $(GLOSSARY code |
| point). |
| */ |
| void popFront(T)(ref T[] a) @safe pure nothrow @nogc |
| if (!isNarrowString!(T[]) && !is(T[] == void[])) |
| { |
| assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof); |
| a = a[1 .. $]; |
| } |
| |
| /// |
| @safe pure nothrow unittest |
| { |
| auto a = [ 1, 2, 3 ]; |
| a.popFront(); |
| assert(a == [ 2, 3 ]); |
| } |
| |
| version (unittest) |
| { |
| static assert(!is(typeof({ int[4] a; popFront(a); }))); |
| static assert(!is(typeof({ immutable int[] a; popFront(a); }))); |
| static assert(!is(typeof({ void[] a; popFront(a); }))); |
| } |
| |
| /// ditto |
| void popFront(C)(ref C[] str) @trusted pure nothrow |
| if (isNarrowString!(C[])) |
| { |
| import std.algorithm.comparison : min; |
| |
| assert(str.length, "Attempting to popFront() past the end of an array of " ~ C.stringof); |
| |
| static if (is(Unqual!C == char)) |
| { |
| static immutable ubyte[] charWidthTab = [ |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
| 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 |
| ]; |
| |
| immutable c = str[0]; |
| if (c < 192) |
| { |
| str = str.ptr[1 .. str.length]; |
| } |
| else |
| { |
| str = str.ptr[min(str.length, charWidthTab.ptr[c - 192]) .. str.length]; |
| } |
| |
| } |
| else static if (is(Unqual!C == wchar)) |
| { |
| immutable u = str[0]; |
| immutable seqLen = 1 + (u >= 0xD800 && u <= 0xDBFF); |
| str = str.ptr[min(seqLen, str.length) .. str.length]; |
| } |
| else static assert(0, "Bad template constraint."); |
| } |
| |
| @safe pure unittest |
| { |
| import std.meta : AliasSeq; |
| |
| foreach (S; AliasSeq!(string, wstring, dstring)) |
| { |
| S s = "\xC2\xA9hello"; |
| s.popFront(); |
| assert(s == "hello"); |
| |
| S str = "hello\U00010143\u0100\U00010143"; |
| foreach (dchar c; ['h', 'e', 'l', 'l', 'o', '\U00010143', '\u0100', '\U00010143']) |
| { |
| assert(str.front == c); |
| str.popFront(); |
| } |
| assert(str.empty); |
| |
| static assert(!is(typeof({ immutable S a; popFront(a); }))); |
| static assert(!is(typeof({ typeof(S.init[0])[4] a; popFront(a); }))); |
| } |
| |
| C[] _eatString(C)(C[] str) |
| { |
| while (!str.empty) |
| str.popFront(); |
| |
| return str; |
| } |
| enum checkCTFE = _eatString("ウェブサイト@La_Verité.com"); |
| static assert(checkCTFE.empty); |
| enum checkCTFEW = _eatString("ウェブサイト@La_Verité.com"w); |
| static assert(checkCTFEW.empty); |
| } |
| |
| @safe unittest // issue 16090 |
| { |
| string s = "\u00E4"; |
| assert(s.length == 2); |
| s = s[0 .. 1]; |
| assert(s.length == 1); |
| s.popFront; |
| assert(s.empty); |
| } |
| |
| @safe unittest |
| { |
| wstring s = "\U00010000"; |
| assert(s.length == 2); |
| s = s[0 .. 1]; |
| assert(s.length == 1); |
| s.popFront; |
| assert(s.empty); |
| } |
| |
| /** |
| Implements the range interface primitive $(D popBack) for built-in |
| arrays. Due to the fact that nonmember functions can be called with |
| the first argument using the dot notation, $(D array.popBack) is |
| equivalent to $(D popBack(array)). For $(GLOSSARY narrow strings), $(D |
| popFront) automatically eliminates the last $(GLOSSARY code point). |
| */ |
| void popBack(T)(ref T[] a) @safe pure nothrow @nogc |
| if (!isNarrowString!(T[]) && !is(T[] == void[])) |
| { |
| assert(a.length); |
| a = a[0 .. $ - 1]; |
| } |
| |
| /// |
| @safe pure nothrow unittest |
| { |
| auto a = [ 1, 2, 3 ]; |
| a.popBack(); |
| assert(a == [ 1, 2 ]); |
| } |
| |
| version (unittest) |
| { |
| static assert(!is(typeof({ immutable int[] a; popBack(a); }))); |
| static assert(!is(typeof({ int[4] a; popBack(a); }))); |
| static assert(!is(typeof({ void[] a; popBack(a); }))); |
| } |
| |
| /// ditto |
| void popBack(T)(ref T[] a) @safe pure |
| if (isNarrowString!(T[])) |
| { |
| import std.utf : strideBack; |
| assert(a.length, "Attempting to popBack() past the front of an array of " ~ T.stringof); |
| a = a[0 .. $ - strideBack(a, $)]; |
| } |
| |
| @safe pure unittest |
| { |
| import std.meta : AliasSeq; |
| |
| foreach (S; AliasSeq!(string, wstring, dstring)) |
| { |
| S s = "hello\xE2\x89\xA0"; |
| s.popBack(); |
| assert(s == "hello"); |
| S s3 = "\xE2\x89\xA0"; |
| auto c = s3.back; |
| assert(c == cast(dchar)'\u2260'); |
| s3.popBack(); |
| assert(s3 == ""); |
| |
| S str = "\U00010143\u0100\U00010143hello"; |
| foreach (dchar ch; ['o', 'l', 'l', 'e', 'h', '\U00010143', '\u0100', '\U00010143']) |
| { |
| assert(str.back == ch); |
| str.popBack(); |
| } |
| assert(str.empty); |
| |
| static assert(!is(typeof({ immutable S a; popBack(a); }))); |
| static assert(!is(typeof({ typeof(S.init[0])[4] a; popBack(a); }))); |
| } |
| } |
| |
| /** |
| Implements the range interface primitive $(D front) for built-in |
| arrays. Due to the fact that nonmember functions can be called with |
| the first argument using the dot notation, $(D array.front) is |
| equivalent to $(D front(array)). For $(GLOSSARY narrow strings), $(D |
| front) automatically returns the first $(GLOSSARY code point) as _a $(D |
| dchar). |
| */ |
| @property ref T front(T)(T[] a) @safe pure nothrow @nogc |
| if (!isNarrowString!(T[]) && !is(T[] == void[])) |
| { |
| assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); |
| return a[0]; |
| } |
| |
| /// |
| @safe pure nothrow unittest |
| { |
| int[] a = [ 1, 2, 3 ]; |
| assert(a.front == 1); |
| } |
| |
| @safe pure nothrow unittest |
| { |
| auto a = [ 1, 2 ]; |
| a.front = 4; |
| assert(a.front == 4); |
| assert(a == [ 4, 2 ]); |
| |
| immutable b = [ 1, 2 ]; |
| assert(b.front == 1); |
| |
| int[2] c = [ 1, 2 ]; |
| assert(c.front == 1); |
| } |
| |
| /// ditto |
| @property dchar front(T)(T[] a) @safe pure |
| if (isNarrowString!(T[])) |
| { |
| import std.utf : decode; |
| assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); |
| size_t i = 0; |
| return decode(a, i); |
| } |
| |
| /** |
| Implements the range interface primitive $(D back) for built-in |
| arrays. Due to the fact that nonmember functions can be called with |
| the first argument using the dot notation, $(D array.back) is |
| equivalent to $(D back(array)). For $(GLOSSARY narrow strings), $(D |
| back) automatically returns the last $(GLOSSARY code point) as _a $(D |
| dchar). |
| */ |
| @property ref T back(T)(T[] a) @safe pure nothrow @nogc |
| if (!isNarrowString!(T[]) && !is(T[] == void[])) |
| { |
| assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); |
| return a[$ - 1]; |
| } |
| |
| /// |
| @safe pure nothrow unittest |
| { |
| int[] a = [ 1, 2, 3 ]; |
| assert(a.back == 3); |
| a.back += 4; |
| assert(a.back == 7); |
| } |
| |
| @safe pure nothrow unittest |
| { |
| immutable b = [ 1, 2, 3 ]; |
| assert(b.back == 3); |
| |
| int[3] c = [ 1, 2, 3 ]; |
| assert(c.back == 3); |
| } |
| |
| /// ditto |
| // Specialization for strings |
| @property dchar back(T)(T[] a) @safe pure |
| if (isNarrowString!(T[])) |
| { |
| import std.utf : decode, strideBack; |
| assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); |
| size_t i = a.length - strideBack(a, a.length); |
| return decode(a, i); |
| } |