diff options
Diffstat (limited to 'libphobos/src/std/range/package.d')
-rw-r--r-- | libphobos/src/std/range/package.d | 286 |
1 files changed, 186 insertions, 100 deletions
diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index a21f4d0..9197134 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -235,7 +235,7 @@ public import std.range.primitives; public import std.typecons : Flag, Yes, No; import std.internal.attributes : betterC; -import std.meta : allSatisfy, staticMap; +import std.meta : allSatisfy, anySatisfy, staticMap; import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral, isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf; @@ -1412,7 +1412,8 @@ auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2) if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) { - return ChooseResult!(R1, R2)(condition, r1, r2); + size_t choice = condition? 0: 1; + return ChooseResult!(R1, R2)(choice, r1, r2); } /// @@ -1447,76 +1448,102 @@ if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && } -private struct ChooseResult(R1, R2) +private struct ChooseResult(Ranges...) { - import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor; + import std.meta : aliasSeqOf, ApplyLeft; + import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor, + lvalueOf; private union { - R1 r1; - R2 r2; + Ranges rs; } - private bool r1Chosen; + private size_t chosenI; - private static auto ref actOnChosen(alias foo, ExtraArgs ...)(ref ChooseResult r, - auto ref ExtraArgs extraArgs) + private static auto ref actOnChosen(alias foo, ExtraArgs ...) + (ref ChooseResult r, auto ref ExtraArgs extraArgs) { - if (r.r1Chosen) - { - ref get1(return ref ChooseResult r) @trusted { return r.r1; } - return foo(get1(r), extraArgs); - } - else + ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } + + switch (r.chosenI) { - ref get2(return ref ChooseResult r) @trusted { return r.r2; } - return foo(get2(r), extraArgs); + static foreach (candI; 0 .. rs.length) + { + case candI: return foo(getI!candI(r), extraArgs); + } + + default: assert(false); } } - this(bool r1Chosen, return scope R1 r1, return scope R2 r2) @trusted + // @trusted because of assignment of r which overlap each other + this(size_t chosen, return scope Ranges rs) @trusted { - // @trusted because of assignment of r1 and r2 which overlap each other import core.lifetime : emplace; - // This should be the only place r1Chosen is ever assigned + // This should be the only place chosenI is ever assigned // independently - this.r1Chosen = r1Chosen; - if (r1Chosen) + this.chosenI = chosen; + + // Otherwise the compiler will complain about skipping these fields + static foreach (i; 0 .. rs.length) { - this.r2 = R2.init; - emplace(&this.r1, r1); + this.rs[i] = Ranges[i].init; } - else + + // The relevant field needs to be initialized last so it will overwrite + // the other initializations and not the other way around. + sw: switch (chosenI) { - this.r1 = R1.init; - emplace(&this.r2, r2); + static foreach (i; 0 .. rs.length) + { + case i: + emplace(&this.rs[i], rs[i]); + break sw; + } + + default: assert(false); } } + // Some legacy code may still call this with typeof(choose(/*...*/))(/*...*/) + // without this overload the regular constructor would invert the meaning of + // the boolean + static if (rs.length == 2) + pragma(inline, true) + deprecated("Call with size_t (0 = first), or use the choose function") + this(bool firstChosen, Ranges rs) + { + import core.lifetime : move; + this(cast(size_t)(firstChosen? 0: 1), rs[0].move, rs[1].move); + } + void opAssign(ChooseResult r) { - static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2) - if (r1Chosen != r.r1Chosen) - { - // destroy the current item - actOnChosen!((ref r) => destroy(r))(this); - } - r1Chosen = r.r1Chosen; - if (r1Chosen) + ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } + + static if (anySatisfy!(hasElaborateDestructor, Ranges)) + if (chosenI != r.chosenI) { - ref get1(return ref ChooseResult r) @trusted { return r.r1; } - get1(this) = get1(r); + // destroy the current item + actOnChosen!((ref r) => destroy(r))(this); } - else + chosenI = r.chosenI; + + sw: switch (chosenI) { - ref get2(return ref ChooseResult r) @trusted { return r.r2; } - get2(this) = get2(r); + static foreach (candI; 0 .. rs.length) + { + case candI: getI!candI(this) = getI!candI(r); + break sw; + } + + default: assert(false); } } // Carefully defined postblit to postblit the appropriate range - static if (hasElaborateCopyConstructor!R1 - || hasElaborateCopyConstructor!R2) + static if (anySatisfy!(hasElaborateCopyConstructor, Ranges)) this(this) { actOnChosen!((ref r) { @@ -1524,20 +1551,18 @@ private struct ChooseResult(R1, R2) })(this); } - static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2) + static if (anySatisfy!(hasElaborateDestructor, Ranges)) ~this() { actOnChosen!((ref r) => destroy(r))(this); } - static if (isInfinite!R1 && isInfinite!R2) - // Propagate infiniteness. - enum bool empty = false; - else - @property bool empty() - { - return actOnChosen!(r => r.empty)(this); - } + // Propagate infiniteness. + static if (allSatisfy!(isInfinite, Ranges)) enum bool empty = false; + else @property bool empty() + { + return actOnChosen!(r => r.empty)(this); + } @property auto ref front() { @@ -1550,34 +1575,38 @@ private struct ChooseResult(R1, R2) return actOnChosen!((ref r) { r.popFront; })(this); } - static if (isForwardRange!R1 && isForwardRange!R2) - @property auto save() return scope + static if (allSatisfy!(isForwardRange, Ranges)) + @property auto save() // return scope inferred { - if (r1Chosen) + auto saveOrInit(size_t i)() { - ref R1 getR1() @trusted { return r1; } - return ChooseResult(r1Chosen, getR1.save, R2.init); - } - else - { - ref R2 getR2() @trusted { return r2; } - return ChooseResult(r1Chosen, R1.init, getR2.save); + ref getI() @trusted { return rs[i]; } + if (i == chosenI) return getI().save; + else return Ranges[i].init; } + + return typeof(this)(chosenI, staticMap!(saveOrInit, + aliasSeqOf!(rs.length.iota))); } - @property void front(T)(T v) - if (is(typeof({ r1.front = v; r2.front = v; }))) + template front(T) { - actOnChosen!((ref r, T v) { r.front = v; })(this, v); - } + private enum overloadValidFor(alias r) = is(typeof(r.front = T.init)); - static if (hasMobileElements!R1 && hasMobileElements!R2) - auto moveFront() + static if (allSatisfy!(overloadValidFor, rs)) + void front(T v) { - return actOnChosen!((ref r) => r.moveFront)(this); + actOnChosen!((ref r, T v) { r.front = v; })(this, v); } + } - static if (isBidirectionalRange!R1 && isBidirectionalRange!R2) + static if (allSatisfy!(hasMobileElements, Ranges)) + auto moveFront() + { + return actOnChosen!((ref r) => r.moveFront)(this); + } + + static if (allSatisfy!(isBidirectionalRange, Ranges)) { @property auto ref back() { @@ -1590,20 +1619,25 @@ private struct ChooseResult(R1, R2) actOnChosen!((ref r) { r.popBack; })(this); } - static if (hasMobileElements!R1 && hasMobileElements!R2) - auto moveBack() - { - return actOnChosen!((ref r) => r.moveBack)(this); - } + static if (allSatisfy!(hasMobileElements, Ranges)) + auto moveBack() + { + return actOnChosen!((ref r) => r.moveBack)(this); + } - @property void back(T)(T v) - if (is(typeof({ r1.back = v; r2.back = v; }))) + template back(T) { - actOnChosen!((ref r, T v) { r.back = v; })(this, v); + private enum overloadValidFor(alias r) = is(typeof(r.back = T.init)); + + static if (allSatisfy!(overloadValidFor, rs)) + void back(T v) + { + actOnChosen!((ref r, T v) { r.back = v; })(this, v); + } } } - static if (hasLength!R1 && hasLength!R2) + static if (allSatisfy!(hasLength, Ranges)) { @property size_t length() { @@ -1612,7 +1646,7 @@ private struct ChooseResult(R1, R2) alias opDollar = length; } - static if (isRandomAccessRange!R1 && isRandomAccessRange!R2) + static if (allSatisfy!(isRandomAccessRange, Ranges)) { auto ref opIndex(size_t index) { @@ -1620,33 +1654,41 @@ private struct ChooseResult(R1, R2) return actOnChosen!get(this, index); } - static if (hasMobileElements!R1 && hasMobileElements!R2) + static if (allSatisfy!(hasMobileElements, Ranges)) auto moveAt(size_t index) { return actOnChosen!((ref r, size_t index) => r.moveAt(index)) (this, index); } - void opIndexAssign(T)(T v, size_t index) - if (is(typeof({ r1[1] = v; r2[1] = v; }))) + private enum indexAssignable(T, R) = is(typeof(lvalueOf!R[1] = T.init)); + + template opIndexAssign(T) + if (allSatisfy!(ApplyLeft!(indexAssignable, T), Ranges)) { - return actOnChosen!((ref r, size_t index, T v) { r[index] = v; }) - (this, index, v); + void opIndexAssign(T v, size_t index) + { + return actOnChosen!((ref r, size_t index, T v) { r[index] = v; }) + (this, index, v); + } } } - static if (hasSlicing!R1 && hasSlicing!R2) - auto opSlice(size_t begin, size_t end) + static if (allSatisfy!(hasSlicing, Ranges)) + auto opSlice(size_t begin, size_t end) + { + alias Slice(R) = typeof(R.init[0 .. 1]); + alias Slices = staticMap!(Slice, Ranges); + + auto sliceOrInit(size_t i)() { - alias Slice1 = typeof(R1.init[0 .. 1]); - alias Slice2 = typeof(R2.init[0 .. 1]); - return actOnChosen!((r, size_t begin, size_t end) { - static if (is(typeof(r) == Slice1)) - return choose(true, r[begin .. end], Slice2.init); - else - return choose(false, Slice1.init, r[begin .. end]); - })(this, begin, end); + ref getI() @trusted { return rs[i]; } + return i == chosenI? getI()[begin .. end]: Slices[i].init; } + + return chooseAmong(chosenI, staticMap!(sliceOrInit, + aliasSeqOf!(rs.length.iota))); + } } // https://issues.dlang.org/show_bug.cgi?id=18657 @@ -1668,8 +1710,9 @@ pure @safe unittest int front; bool empty; void popFront() {} - @property R save() { p = q; return this; } - // `p = q;` is only there to prevent inference of `scope return`. + // `p = q;` is only there to prevent inference of `scope return`. + @property @safe R save() { p = q; return this; } + } R r; choose(true, r, r).save; @@ -1801,10 +1844,7 @@ if (Ranges.length >= 2 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && !is(CommonType!(staticMap!(ElementType, Ranges)) == void)) { - static if (Ranges.length == 2) - return choose(index == 0, rs[0], rs[1]); - else - return choose(index == 0, rs[0], chooseAmong(index - 1, rs[1 .. $])); + return ChooseResult!Ranges(index, rs); } /// @@ -13521,3 +13561,49 @@ pure @safe unittest assert([1, 2, 3, 4].padRight(0, 10)[7 .. 9].equal([0, 0])); } + +/** +This simplifies a commonly used idiom in phobos for accepting any kind of string +parameter. The type `R` can for example be a simple string, chained string using +$(REF chain, std,range), $(REF chainPath, std,path) or any other input range of +characters. + +Only finite length character ranges are allowed with this constraint. + +This template is equivalent to: +--- +isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) +--- + +See_Also: +$(REF isInputRange, std,range,primitives), +$(REF isInfinite, std,range,primitives), +$(LREF isSomeChar), +$(REF ElementEncodingType, std,range,primitives) +*/ +template isSomeFiniteCharInputRange(R) +{ + import std.traits : isSomeChar; + + enum isSomeFiniteCharInputRange = isInputRange!R && !isInfinite!R + && isSomeChar!(ElementEncodingType!R); +} + +/// +@safe unittest +{ + import std.path : chainPath; + import std.range : chain; + + void someLibraryMethod(R)(R argument) + if (isSomeFiniteCharInputRange!R) + { + // implementation detail, would iterate over each character of argument + } + + someLibraryMethod("simple strings work"); + someLibraryMethod(chain("chained", " ", "strings", " ", "work")); + someLibraryMethod(chainPath("chained", "paths", "work")); + // you can also use custom structs implementing a char range +} + |