From 811010c845324280bbd6c66541264385a8e4573c Mon Sep 17 00:00:00 2001 From: Paul Backus Date: Thu, 27 Feb 2025 15:18:00 -0500 Subject: [PATCH 1/3] std.conv: factor out writeTextImpl from textImpl --- std/conv.d | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/std/conv.d b/std/conv.d index 9c7d914fa8c..b1bb5e1019d 100644 --- a/std/conv.d +++ b/std/conv.d @@ -5096,30 +5096,35 @@ private S textImpl(S, U...)(U args) // 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)); - } - + app.writeTextImpl!S(args); return app.data; } } +private void writeTextImpl(S, Sink, U...)(ref Sink sink, U args) +if (isSomeString!S && isOutputRange!(Sink, ElementEncodingType!S)) +{ + // 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))) ) + ) + put(sink, 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 + put(sink, textImpl!(S)(arg)); + else + put(sink, to!S(arg)); + } +} + /*************************************************************** The `octal` facility provides a means to declare a number in base 8. From 41b40919768e6380acf5a1e171f947ac5f803666 Mon Sep 17 00:00:00 2001 From: Paul Backus Date: Thu, 27 Feb 2025 16:05:04 -0500 Subject: [PATCH 2/3] std.conv: add writeText, writeWText, writeDText These are variants of text, wtext, and dtext that write their output to an output range instead of returning a string. Fixes #10550 --- std/conv.d | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/std/conv.d b/std/conv.d index b1bb5e1019d..e38646ad09a 100644 --- a/std/conv.d +++ b/std/conv.d @@ -19,6 +19,9 @@ $(TR $(TD Strings) $(TD $(LREF text) $(LREF wtext) $(LREF dtext) + $(LREF writeText) + $(LREF writeWText) + $(LREF writeDText) $(LREF hexString) )) $(TR $(TD Numeric) $(TD @@ -5076,6 +5079,75 @@ if (T.length > 0) { return textImpl!dstring(args); } assert(text(dg4) == "bool delegate(bool, int) @trusted"); } +/// Convenience functions for writing arguments to an output range as text. +void writeText(Sink, T...)(ref Sink sink, T args) +if (isOutputRange!(Sink, char) && T.length > 0) +{ + sink.writeTextImpl!string(args); +} + +/// ditto +void writeWText(Sink, T...)(ref Sink sink, T args) +if (isOutputRange!(Sink, wchar) && T.length > 0) +{ + sink.writeTextImpl!wstring(args); +} + +/// ditto +void writeDText(Sink, T...)(ref Sink sink, T args) +if (isOutputRange!(Sink, dchar) && T.length > 0) +{ + sink.writeTextImpl!dstring(args); +} + +/// +@safe unittest +{ + import std.array : appender; + + auto output = appender!string(); + output.writeText("The answer is ", 42); + + assert(output.data == "The answer is 42"); +} + +/// +@safe unittest +{ + import std.array : appender; + + const color = "red"; + auto output = appender!string(); + output.writeText(i"My favorite color is $(color)"); + + assert(output.data == "My favorite color is red"); +} + +@safe unittest +{ + auto capp = appender!string(); + auto wapp = appender!wstring(); + auto dapp = appender!dstring(); + + capp.writeText(42, ' ', 1.5, ": xyz"); + wapp.writeWText(42, ' ', 1.5, ": xyz"); + dapp.writeDText(42, ' ', 1.5, ": xyz"); + + assert(capp.data == "42 1.5: xyz"c); + assert(wapp.data == "42 1.5: xyz"w); + assert(dapp.data == "42 1.5: xyz"d); +} + +// Check range API compliance using OutputRange interface +@system unittest +{ + import std.range.interfaces : OutputRange, outputRangeObject; + import std.range : nullSink; + + OutputRange!char testOutput = outputRangeObject!char(nullSink); + testOutput.writeText(42, ' ', 1.5, ": xyz"); +} + private S textImpl(S, U...)(U args) { static if (U.length == 0) From ac0b42c3c5e0a4b31a78bf190d75507f91b702c5 Mon Sep 17 00:00:00 2001 From: Paul Backus Date: Fri, 28 Feb 2025 20:56:47 -0500 Subject: [PATCH 3/3] Add changelog entry for writeText --- changelog/write-text.dd | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 changelog/write-text.dd diff --git a/changelog/write-text.dd b/changelog/write-text.dd new file mode 100644 index 00000000000..ba161c3a413 --- /dev/null +++ b/changelog/write-text.dd @@ -0,0 +1,20 @@ +Add `writeText`, `writeWText`, and `writeDText` to `std.conv` + +These functions are variants of the existing `text`, `wtext`, and `dtext` +functions. Instead of returning a string, they write their output to an output +range. + +Like `text`, `writeText` can accept an +$(LINK2 $(ROOT_DIR)spec/istring.html, interpolated expression sequence) as an +argument. + +Example: + +--- +import std.conv : writeText; +import std.array : appender; + +auto output = appender!string(); +output.writeText(i"2 + 2 == $(2 + 2)"); +assert(output.data == "2 + 2 == 4"); +---