Bug 4888: Heavy reliance on Bug 3534 in Phobos range usage. I used a different approach here than the first one I tried. I only did what was necessary to make Phobos work instead of trying to fix the deeper issue of making std.algorithm to work w/ const/immutable arrays.

This commit is contained in:
David Simcha 2010-09-18 21:00:52 +00:00
parent fe6cbe460b
commit b597d23f83
8 changed files with 105 additions and 69 deletions

View file

@ -111,14 +111,15 @@ template map(fun...)
} }
} }
struct Map(alias fun, Range) if (isInputRange!(Range)) struct Map(alias fun, Range) if (isInputRange!(Unqual!Range))
{ {
alias Unqual!Range R;
alias fun _fun; alias fun _fun;
alias typeof({ return _fun(.ElementType!(Range).init); }()) ElementType; alias typeof({ return _fun(.ElementType!(R).init); }()) ElementType;
Unqual!Range _input; R _input;
Unqual!ElementType _cache; Unqual!ElementType _cache;
static if (isBidirectionalRange!(Range)) static if (isBidirectionalRange!(R))
{ {
// Using a second cache would lead to at least 1 extra function evaluation // Using a second cache would lead to at least 1 extra function evaluation
// and wasted space when 99% of the time this range will only be iterated // and wasted space when 99% of the time this range will only be iterated
@ -152,19 +153,19 @@ struct Map(alias fun, Range) if (isInputRange!(Range))
{ {
if (!_input.empty) _cache = _fun(_input.front); if (!_input.empty) _cache = _fun(_input.front);
static if(isBidirectionalRange!(Range)) static if(isBidirectionalRange!(R))
{ {
cacheIsBack_ = false; cacheIsBack_ = false;
} }
} }
this(Range input) this(R input)
{ {
_input = input; _input = input;
fillCache; fillCache;
} }
static if (isInfinite!Range) static if (isInfinite!R)
{ {
// Propagate infinite-ness. // Propagate infinite-ness.
enum bool empty = false; enum bool empty = false;
@ -185,7 +186,7 @@ struct Map(alias fun, Range) if (isInputRange!(Range))
@property ElementType front() @property ElementType front()
{ {
static if (isBidirectionalRange!(Range)) static if (isBidirectionalRange!(R))
{ {
if (cacheIsBack_) if (cacheIsBack_)
{ {
@ -195,7 +196,7 @@ struct Map(alias fun, Range) if (isInputRange!(Range))
return _cache; return _cache;
} }
static if (isRandomAccessRange!Range) static if (isRandomAccessRange!R)
{ {
ElementType opIndex(size_t index) ElementType opIndex(size_t index)
{ {
@ -213,7 +214,7 @@ struct Map(alias fun, Range) if (isInputRange!(Range))
} }
} }
static if (hasSlicing!(Range)) static if (hasSlicing!(R))
{ {
typeof(this) opSlice(size_t lowerBound, size_t upperBound) typeof(this) opSlice(size_t lowerBound, size_t upperBound)
{ {
@ -221,7 +222,7 @@ struct Map(alias fun, Range) if (isInputRange!(Range))
} }
} }
static if (isForwardRange!Range) static if (isForwardRange!R)
@property Map save() @property Map save()
{ {
auto result = this; auto result = this;
@ -850,11 +851,12 @@ filter(alias pred, Range)(Range rs)
return typeof(return)(rs); return typeof(return)(rs);
} }
struct Filter(alias pred, Range) if (isInputRange!(Range)) struct Filter(alias pred, Range) if (isInputRange!(Unqual!Range))
{ {
Unqual!Range _input; alias Unqual!Range R;
R _input;
this(Range r) this(R r)
{ {
_input = r; _input = r;
while (!_input.empty && !pred(_input.front)) _input.popFront; while (!_input.empty && !pred(_input.front)) _input.popFront;
@ -879,7 +881,7 @@ struct Filter(alias pred, Range) if (isInputRange!(Range))
return _input.front; return _input.front;
} }
static if(isForwardRange!Range) static if(isForwardRange!R)
{ {
@property typeof(this) save() @property typeof(this) save()
{ {

View file

@ -253,6 +253,10 @@ void main()
return a; return a;
} }
private template notConst(T) {
enum notConst = !is(T == const) && !is(T == immutable);
}
/** /**
Implements the range interface primitive $(D popFront) for built-in Implements the range interface primitive $(D popFront) for built-in
arrays. Due to the fact that nonmember functions can be called with arrays. Due to the fact that nonmember functions can be called with
@ -271,8 +275,10 @@ void main()
---- ----
*/ */
void popFront(T)(ref T[] a) if (!is(Unqual!T == char) && !is(Unqual!T == wchar)) void popFront(A)(ref A a)
if(!isNarrowString!A && isDynamicArray!A && notConst!A)
{ {
alias typeof(A[0]) T;
assert(a.length, "Attempting to popFront() past the end of an array of " assert(a.length, "Attempting to popFront() past the end of an array of "
~ T.stringof); ~ T.stringof);
a = a[1 .. $]; a = a[1 .. $];
@ -285,10 +291,14 @@ unittest
int[] a = [ 1, 2, 3 ]; int[] a = [ 1, 2, 3 ];
a.popFront; a.popFront;
assert(a == [ 2, 3 ]); assert(a == [ 2, 3 ]);
static assert(!__traits(compiles, popFront!(immutable int[])));
} }
void popFront(T)(ref T[] a) if (is(Unqual!T == char) || is(Unqual!T == wchar)) void popFront(A)(ref A a)
if(isNarrowString!A && notConst!A)
{ {
alias typeof(a[0]) T;
assert(a.length, "Attempting to popFront() past the end of an array of " assert(a.length, "Attempting to popFront() past the end of an array of "
~ T.stringof); ~ T.stringof);
a = a[std.utf.stride(a, 0) .. $]; a = a[std.utf.stride(a, 0) .. $];
@ -304,6 +314,8 @@ unittest
assert(s2 == "hello"); assert(s2 == "hello");
string s3 = "\u20AC100"; string s3 = "\u20AC100";
//write(s3, '\n'); //write(s3, '\n');
static assert(!__traits(compiles, popFront!(immutable string)));
} }
/** /**
@ -324,7 +336,8 @@ void main()
---- ----
*/ */
void popBack(T)(ref T[] a) if (!is(Unqual!T == char) && !is(Unqual!T == wchar)) void popBack(A)(ref A a)
if(isDynamicArray!A && !isNarrowString!A && notConst!A)
{ {
assert(a.length); assert(a.length);
a = a[0 .. $ - 1]; a = a[0 .. $ - 1];
@ -337,9 +350,12 @@ unittest
int[] a = [ 1, 2, 3 ]; int[] a = [ 1, 2, 3 ];
a.popBack; a.popBack;
assert(a == [ 1, 2 ]); assert(a == [ 1, 2 ]);
static assert(!__traits(compiles, popBack!(immutable int[])));
} }
void popBack(T)(ref T[] a) if (is(Unqual!T == char)) void popBack(A)(ref A a)
if(is(A : const(char)[]) && notConst!A)
{ {
immutable n = a.length; immutable n = a.length;
const p = a.ptr + n; const p = a.ptr + n;
@ -376,9 +392,12 @@ unittest
assert(c == cast(dchar)'\u2260'); assert(c == cast(dchar)'\u2260');
s3.popBack(); s3.popBack();
assert(s3 == ""); assert(s3 == "");
static assert(!__traits(compiles, popBack!(immutable char[])));
} }
void popBack(T)(ref T[] a) if (is(Unqual!T == wchar)) void popBack(A)(ref A a)
if(is(A : const(wchar)[]) && notConst!A)
{ {
assert(a.length); assert(a.length);
if (a.length == 1) if (a.length == 1)
@ -395,6 +414,8 @@ unittest
wstring s = "hello\xE2\x89\xA0"; wstring s = "hello\xE2\x89\xA0";
s.popBack(); s.popBack();
assert(s == "hello"); assert(s == "hello");
static assert(!__traits(compiles, popBack!(immutable wchar[])));
} }
/** /**
@ -837,6 +858,13 @@ Appends one item to the managed array.
return newext > newlength ? newext : newlength; return newext > newlength ? newext : newlength;
} }
// Const fixing hack.
void put(Range)(Range items)
if(isInputRange!(Unqual!Range) && !isInputRange!Range) {
alias put!(Unqual!Range) p;
p(items);
}
/** /**
Appends an entire range to the managed array. Appends an entire range to the managed array.
*/ */

View file

@ -108,7 +108,7 @@ assert(b == "abc"w);
---- ----
*/ */
T toImpl(T, S)(S s) if (!implicitlyConverts!(S, T) && isSomeString!T T toImpl(T, S)(S s) if (!implicitlyConverts!(S, T) && isSomeString!T
&& isInputRange!S && isSomeChar!(ElementType!S)) && isInputRange!(Unqual!S) && isSomeChar!(ElementType!S))
{ {
static if (isSomeString!S) static if (isSomeString!S)
{ {
@ -182,8 +182,13 @@ converted by calling $(D to!T).
*/ */
T toImpl(T, S)(S s, in T leftBracket = "[", in T separator = " ", T toImpl(T, S)(S s, in T leftBracket = "[", in T separator = " ",
in T rightBracket = "]") in T rightBracket = "]")
if (isSomeString!T && !isSomeChar!(ElementType!S) && isInputRange!S) if (isSomeString!T && !isSomeChar!(ElementType!S) &&
(isInputRange!S || isInputRange!(Unqual!S)))
{ {
static if(!isInputRange!S) {
alias toImpl!(T, Unqual!S) ti;
return ti(s, leftBracket, separator, rightBracket);
} else {
alias Unqual!(typeof(T.init[0])) Char; alias Unqual!(typeof(T.init[0])) Char;
// array-to-string conversion // array-to-string conversion
auto result = appender!(Char[])(); auto result = appender!(Char[])();
@ -204,6 +209,7 @@ if (isSomeString!T && !isSomeChar!(ElementType!S) && isInputRange!S)
result.put(rightBracket); result.put(rightBracket);
return cast(T) result.data; return cast(T) result.data;
} }
}
/* /*
Converting static arrays forwards to their dynamic counterparts. Converting static arrays forwards to their dynamic counterparts.

View file

@ -57,7 +57,7 @@ struct JSONValue {
/** /**
Parses a serialized string and returns a tree of JSON values. Parses a serialized string and returns a tree of JSON values.
*/ */
JSONValue parseJSON(T)(in T json, int maxDepth = -1) if(isInputRange!T) { JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) {
JSONValue root = void; JSONValue root = void;
root.type = JSON_TYPE.NULL; root.type = JSON_TYPE.NULL;
@ -427,7 +427,7 @@ unittest {
// then use the resulting values tree to generate an identical // then use the resulting values tree to generate an identical
// serialization, both the decoder and encoder works. // serialization, both the decoder and encoder works.
immutable jsons = [ auto jsons = [
`null`, `null`,
`true`, `true`,
`false`, `false`,

View file

@ -2467,54 +2467,54 @@ void inverseFft(Ret, R)(R range, Ret buf) {
unittest { unittest {
// Test values from R. // Test values from R.
const arr = [1,2,3,4,5,6,7,8]; auto arr = [1,2,3,4,5,6,7,8];
const fft1 = fft(arr); auto fft1 = fft(arr);
assert(approxEqual(map!"a.re"(fft1), assert(approxEqual(map!"a.re"(fft1),
[36.0, -4, -4, -4, -4, -4, -4, -4])); [36.0, -4, -4, -4, -4, -4, -4, -4]));
assert(approxEqual(map!"a.im"(fft1), assert(approxEqual(map!"a.im"(fft1),
[0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568])); [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568]));
alias Complex!float C; alias Complex!float C;
const arr2 = [C(1,2), C(3,4), C(5,6), C(7,8), C(9,10), auto arr2 = [C(1,2), C(3,4), C(5,6), C(7,8), C(9,10),
C(11,12), C(13,14), C(15,16)]; C(11,12), C(13,14), C(15,16)];
const fft2 = fft(arr2); auto fft2 = fft(arr2);
assert(approxEqual(map!"a.re"(fft2), assert(approxEqual(map!"a.re"(fft2),
[64.0, -27.3137, -16, -11.3137, -8, -4.6862, 0, 11.3137])); [64.0, -27.3137, -16, -11.3137, -8, -4.6862, 0, 11.3137]));
assert(approxEqual(map!"a.im"(fft2), assert(approxEqual(map!"a.im"(fft2),
[72, 11.3137, 0, -4.686, -8, -11.3137, -16, -27.3137])); [72, 11.3137, 0, -4.686, -8, -11.3137, -16, -27.3137]));
const inv1 = inverseFft(fft1); auto inv1 = inverseFft(fft1);
assert(approxEqual(map!"a.re"(inv1), arr)); assert(approxEqual(map!"a.re"(inv1), arr));
assert(reduce!max(map!"a.im"(inv1)) < 1e-10); assert(reduce!max(map!"a.im"(inv1)) < 1e-10);
const inv2 = inverseFft(fft2); auto inv2 = inverseFft(fft2);
assert(approxEqual(map!"a.re"(inv2), map!"a.re"(arr2))); assert(approxEqual(map!"a.re"(inv2), map!"a.re"(arr2)));
assert(approxEqual(map!"a.im"(inv2), map!"a.im"(arr2))); assert(approxEqual(map!"a.im"(inv2), map!"a.im"(arr2)));
// FFTs of size 0, 1 and 2 are handled as special cases. Test them here. // FFTs of size 0, 1 and 2 are handled as special cases. Test them here.
const ushort[] empty; ushort[] empty;
assert(fft(empty) == null); assert(fft(empty) == null);
assert(inverseFft(fft(empty)) == null); assert(inverseFft(fft(empty)) == null);
const real[] oneElem = [4.5L]; real[] oneElem = [4.5L];
const oneFft = fft(oneElem); auto oneFft = fft(oneElem);
assert(oneFft.length == 1); assert(oneFft.length == 1);
assert(oneFft[0].re == 4.5L); assert(oneFft[0].re == 4.5L);
assert(oneFft[0].im == 0); assert(oneFft[0].im == 0);
const oneInv = inverseFft(oneFft); auto oneInv = inverseFft(oneFft);
assert(oneInv.length == 1); assert(oneInv.length == 1);
assert(approxEqual(oneInv[0].re, 4.5)); assert(approxEqual(oneInv[0].re, 4.5));
assert(approxEqual(oneInv[0].im, 0)); assert(approxEqual(oneInv[0].im, 0));
immutable long[2] twoElems = [8, 4]; long[2] twoElems = [8, 4];
const twoFft = fft(twoElems[]); auto twoFft = fft(twoElems[]);
assert(twoFft.length == 2); assert(twoFft.length == 2);
assert(approxEqual(twoFft[0].re, 12)); assert(approxEqual(twoFft[0].re, 12));
assert(approxEqual(twoFft[0].im, 0)); assert(approxEqual(twoFft[0].im, 0));
assert(approxEqual(twoFft[1].re, 4)); assert(approxEqual(twoFft[1].re, 4));
assert(approxEqual(twoFft[1].im, 0)); assert(approxEqual(twoFft[1].im, 0));
const twoInv = inverseFft(twoFft); auto twoInv = inverseFft(twoFft);
assert(approxEqual(twoInv[0].re, 8)); assert(approxEqual(twoInv[0].re, 8));
assert(approxEqual(twoInv[0].im, 0)); assert(approxEqual(twoInv[0].im, 0));
assert(approxEqual(twoInv[1].re, 4)); assert(approxEqual(twoInv[1].re, 4));

View file

@ -3302,8 +3302,8 @@ else
{ {
// Work around DMD bugs 4676, 4652. // Work around DMD bugs 4676, 4652.
auto lockstep(Args...)(Args args) auto lockstep(Args...)(Args args)
if(allSatisfy!(isInputRange, Args) || ( if(allSatisfy!(isInputRange, staticMap!(Unqual, Args)) || (
allSatisfy!(isInputRange, Args[0..$ - 1]) && allSatisfy!(isInputRange, staticMap!(Unqual, Args[0..$ - 1])) &&
is(Args[$ - 1] == StoppingPolicy)) is(Args[$ - 1] == StoppingPolicy))
) )
{ {

View file

@ -541,7 +541,7 @@ Index in $(D s) where $(D sub) is found, $(D -1) if not found.
*/ */
sizediff_t sizediff_t
indexOf(Char1, Char2)(in Char1[] s, in Char2[] sub, indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
CaseSensitive cs = CaseSensitive.yes) CaseSensitive cs = CaseSensitive.yes)
{ {
if (cs == CaseSensitive.yes) if (cs == CaseSensitive.yes)

View file

@ -125,7 +125,7 @@ import std.array;
import std.string; import std.string;
import std.encoding; import std.encoding;
immutable cdata = "<![CDATA["; enum cdata = "<![CDATA[";
/** /**
* Returns true if the character is a character according to the XML standard * Returns true if the character is a character according to the XML standard