diff --git a/std/conv.d b/std/conv.d index 65042b81095..a968083d443 100644 --- a/std/conv.d +++ b/std/conv.d @@ -4792,21 +4792,34 @@ package template WideElementType(T) alias WideElementType = E; } +private enum hasInputRanges(T...) = anySatisfy!(isInputRange, T); /*************************************************************** * Convenience functions for converting one or more arguments * of any type into _text (the three character widths). */ -string text(T...)(T args) -if (T.length > 0) { return textImpl!string(args); } +string text(T...)(const(T) args) +if (T.length > 0 && hasInputRanges!T) { return textImpl!string(args); } + +/// ditto +string text(T...)(const T args) +if (T.length > 0 && !hasInputRanges!T) { return textImpl!string(args); } ///ditto -wstring wtext(T...)(T args) -if (T.length > 0) { return textImpl!wstring(args); } +wstring wtext(T...)(const(T) args) +if (T.length > 0 && hasInputRanges!T) { return textImpl!wstring(args); } ///ditto -dstring dtext(T...)(T args) -if (T.length > 0) { return textImpl!dstring(args); } +wstring wtext(T...)(const T args) +if (T.length > 0 && !hasInputRanges!T) { return textImpl!wstring(args); } + +///ditto +dstring dtext(T...)(const(T) args) +if (T.length > 0 && hasInputRanges!T) { return textImpl!dstring(args); } + +///ditto +dstring dtext(T...)(const T args) +if (T.length > 0 && !hasInputRanges!T) { return textImpl!dstring(args); } /// @safe unittest @@ -4835,11 +4848,51 @@ if (T.length > 0) { return textImpl!dstring(args); } assert(dtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"d); } -private S textImpl(S, U...)(U args) +// ensure that ranges are still printed properly +@safe unittest +{ + static struct Range + { + int counter = 0; + + @safe pure nothrow @nogc: + bool empty() const => (counter <= 0); + int front() const => counter; + void popFront() { --counter; } + } + + auto m = Range(2); + assert(text(m) == "[2, 1]"); + + //const c = Range(3); + //assert(text(c) == "const(Range)(3)"); +} + +// ensure that a usage pattern seen in libraries like "unit-threaded" keeps working +@safe unittest +{ + static final class Foo + { + override string toString() const @safe + { + return ":-)"; + } + } + + const c = new Foo(); + assert(text(c) == ":-)"); + assert(text(c, " ") == ":-) "); +} + +private S textImpl(S, U...)(const(U) args) +if (hasInputRanges!U) { static if (U.length == 0) { - return null; + version (none) + return null; + else + static assert(false, "How can zero-length `U` have InputRanges?"); } else static if (U.length == 1) { @@ -4879,6 +4932,50 @@ private S textImpl(S, U...)(U args) } } +private S textImpl(S, U...)(const U args) +if (!hasInputRanges!U) +{ + static if (U.length == 0) + { + return null; + } + else static if (U.length == 1) + { + return to!S(args[0]); + } + else + { + import std.array : appender; + import std.traits : isSomeChar, isSomeString; + + auto app = appender!S(); + + // assume that on average, parameters will have less + // than 20 elements + app.reserve(U.length * 20); + // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209 + static foreach (arg; args) + { + static if ( + isSomeChar!(typeof(arg)) + || isSomeString!(typeof(arg)) + || ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) ) + ) + app.put(arg); + else static if ( + + is(immutable typeof(arg) == immutable uint) || is(immutable typeof(arg) == immutable ulong) || + is(immutable typeof(arg) == immutable int) || is(immutable typeof(arg) == immutable long) + ) + // https://issues.dlang.org/show_bug.cgi?id=17712#c15 + app.put(textImpl!(S)(arg)); + else + app.put(to!S(arg)); + } + + return app.data; + } +} /*************************************************************** The `octal` facility provides a means to declare a number in base 8.