// Written in the D programming language. /**This is a submodule of $(MREF std, format). It provides two functions for writing formatted output: $(LREFformatValue) and $(LREF formattedWrite). The former writes a singlevalue. The latter writes several values at once, interspersed withunformatted text. The following combinations of format characters and types areavailable: $(BOOKTABLE ,$(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o) $(TH x, X) $(TH e, E, f, F, g, G, a, A) $(TH r) $(TH compound))$(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))$(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))$(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)))$(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)))$(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))$(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))$(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))$(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))$(TR $(TD $(I pointer)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))$(TR $(TD $(I SIMD vectors)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))$(TR $(TD $(I delegates)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))) Enums can be used with all format characters of the base type. $(H3 $(LNAME2 aggregates, Structs, Unions, Classes, and Interfaces)) Aggregate types can define various `toString` functions. If thisfunction takes a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format,spec) or a $(I format string) as argument, the function decideswhich format characters are accepted. If no `toString` is defined andthe aggregate is an $(REF_ALTTEXT input range, isInputRange, std,range, primitives), it is treated like a range, that is $(B 's'), $(B'r') and a compound specifier are accepted. In all other casesaggregate types only accept $(B 's'). `toString` should have one of the following signatures: ---void toString(Writer, Char)(ref Writer w, const ref FormatSpec!Char fmt)void toString(Writer)(ref Writer w)string toString();--- Where `Writer` is an $(REF_ALTTEXT output range, isOutputRange,std,range,primitives) which accepts characters $(LPAREN)of type`Char` in the first version$(RPAREN). The template type does not haveto be called `Writer`. Sometimes it's not possible to use a template, for example when`toString` overrides `Object.toString`. In this case, the following$(LPAREN)slower and less flexible$(RPAREN) functions can be used: ---void toString(void delegate(const(char)[]) sink, const ref FormatSpec!char fmt);void toString(void delegate(const(char)[]) sink, string fmt);void toString(void delegate(const(char)[]) sink);--- When several of the above `toString` versions are available, theversions with `Writer` take precedence over the versions with a`sink`. `string toString()` has the lowest priority. If none of the above mentioned `toString` versions are available, theaggregates will be formatted by other means, in the followingorder: If an aggregate is an $(REF_ALTTEXT input range, isInputRange, std,range, primitives), it is formatted like an input range. If an aggregate is a builtin type (using `alias this`), it is formattedlike the builtin type. If all else fails, structs are formatted like `Type(field1, field2, ...)`,classes and interfaces are formatted with their fully qualified nameand unions with their base name. Copyright: Copyright The D Language Foundation 2000-2013. License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,Andrei Alexandrescu), and Kenji Hara Source: $(PHOBOSSRC std/format/write.d) */module std.format.write; /**`bool`s are formatted as `"true"` or `"false"` with `%s` and like the`byte`s 1 and 0 with all other format characters. */@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, true, spec1); assert(w1.data == "true"); auto w2 = appender!string(); auto spec2 = singleSpec("%#x"); formatValue(w2, true, spec2); assert(w2.data == "0x1");} /// The `null` literal is formatted as `"null"`.@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w = appender!string(); auto spec = singleSpec("%s"); formatValue(w, null, spec); assert(w.data == "null");} /**Integrals are formatted in (signed) every day notation with `%s` and`%d` and as an (unsigned) image of the underlying bit representationwith `%b` (binary), `%u` (decimal), `%o` (octal), and `%x` (hexadecimal). */@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%d"); formatValue(w1, -1337, spec1); assert(w1.data == "-1337"); auto w2 = appender!string(); auto spec2 = singleSpec("%x"); formatValue(w2, -1337, spec2); assert(w2.data == "fffffac7");} /**Floating-point values are formatted in natural notation with `%f`, inscientific notation with `%e`, in short notation with `%g`, and inhexadecimal scientific notation with `%a`. If a rounding mode isavailable, they are rounded according to this rounding mode, otherwisethey are rounded to the nearest value, ties to even. */@safe unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%.3f"); formatValue(w1, 1337.7779, spec1); assert(w1.data == "1337.778"); auto w2 = appender!string(); auto spec2 = singleSpec("%.3e"); formatValue(w2, 1337.7779, spec2); assert(w2.data == "1.338e+03"); auto w3 = appender!string(); auto spec3 = singleSpec("%.3g"); formatValue(w3, 1337.7779, spec3); assert(w3.data == "1.34e+03"); auto w4 = appender!string(); auto spec4 = singleSpec("%.3a"); formatValue(w4, 1337.7779, spec4); assert(w4.data == "0x1.4e7p+10");} /**Individual characters (`char`, `wchar`, or `dchar`) are formatted asUnicode characters with `%s` and `%c` and as integers (`ubyte`,`ushort`, `uint`) with all other format characters. With$(MREF_ALTTEXT compound specifiers, std,format) characters aretreated differently. */@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%c"); formatValue(w1, 'ì', spec1); assert(w1.data == "ì"); auto w2 = appender!string(); auto spec2 = singleSpec("%#x"); formatValue(w2, 'ì', spec2); assert(w2.data == "0xec");} /**Strings are formatted as a sequence of characters with `%s`.Non-printable characters are not escaped. With a compound specifierthe string is treated like a range of characters. With $(MREF_ALTTEXTcompound specifiers, std,format) strings are treated differently. */@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, "hello", spec1); assert(w1.data == "hello"); auto w2 = appender!string(); auto spec2 = singleSpec("%(%#x%|/%)"); formatValue(w2, "hello", spec2); assert(w2.data == "0x68/0x65/0x6c/0x6c/0x6f");} /// Static arrays are formatted as dynamic arrays.@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w = appender!string(); auto spec = singleSpec("%s"); int[2] two = [1, 2]; formatValue(w, two, spec); assert(w.data == "[1, 2]");} /**Dynamic arrays are formatted as input ranges. */@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); auto two = [1, 2]; formatValue(w1, two, spec1); assert(w1.data == "[1, 2]"); auto w2 = appender!string(); auto spec2 = singleSpec("%(%g%|, %)"); auto consts = [3.1415926, 299792458, 6.67430e-11]; formatValue(w2, consts, spec2); assert(w2.data == "3.14159, 2.99792e+08, 6.6743e-11"); // void[] is treated like ubyte[] auto w3 = appender!string(); auto spec3 = singleSpec("%s"); void[] val = cast(void[]) cast(ubyte[])[1, 2, 3]; formatValue(w3, val, spec3); assert(w3.data == "[1, 2, 3]");} /**Associative arrays are formatted by using `':'` and `", "` asseparators, enclosed by `'['` and `']'` when used with `%s`. It'salso possible to use a compound specifier for better control. Please note, that the order of the elements is not defined, thereforethe result of this function might differ. */@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto aa = [10:17.5, 20:9.99]; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, aa, spec1); assert(w1.data == "[10:17.5, 20:9.99]" || w1.data == "[20:9.99, 10:17.5]"); auto w2 = appender!string(); auto spec2 = singleSpec("%(%x = %.0e%| # %)"); formatValue(w2, aa, spec2); assert(w2.data == "a = 2e+01 # 14 = 1e+01" || w2.data == "14 = 1e+01 # a = 2e+01");} /**`enum`s are formatted as their name when used with `%s` and liketheir base value else. */@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; enum A { first, second, third } auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, A.second, spec1); assert(w1.data == "second"); auto w2 = appender!string(); auto spec2 = singleSpec("%d"); formatValue(w2, A.second, spec2); assert(w2.data == "1"); // values of an enum that have no name are formatted with %s using a cast A a = A.third; a++; auto w3 = appender!string(); auto spec3 = singleSpec("%s"); formatValue(w3, a, spec3); assert(w3.data == "cast(A)3");} /**`structs`, `unions`, `classes` and `interfaces` can be formatted inseveral different ways. The following example highlights `struct`formatting, however, it applies to other aggregates as well. */@safe unittest{ import std.array : appender; import std.format.spec : FormatSpec, singleSpec; // Using a `toString` with a writer static struct Point1 { import std.range.primitives : isOutputRange, put; int x, y; void toString(W)(ref W writer, scope const ref FormatSpec!char f) if (isOutputRange!(W, char)) { put(writer, "("); formatValue(writer, x, f); put(writer, ","); formatValue(writer, y, f); put(writer, ")"); } } auto w1 = appender!string(); auto spec1 = singleSpec("%s"); auto p1 = Point1(16, 11); formatValue(w1, p1, spec1); assert(w1.data == "(16,11)"); // Using a `toString` with a sink static struct Point2 { int x, y; void toString(scope void delegate(scope const(char)[]) @safe sink, scope const FormatSpec!char fmt) const { sink("("); sink.formatValue(x, fmt); sink(","); sink.formatValue(y, fmt); sink(")"); } } auto w2 = appender!string(); auto spec2 = singleSpec("%03d"); auto p2 = Point2(16,11); formatValue(w2, p2, spec2); assert(w2.data == "(016,011)"); // Using `string toString()` static struct Point3 { int x, y; string toString() { import std.conv : to; return "(" ~ to!string(x) ~ "," ~ to!string(y) ~ ")"; } } auto w3 = appender!string(); auto spec3 = singleSpec("%s"); // has to be %s auto p3 = Point3(16,11); formatValue(w3, p3, spec3); assert(w3.data == "(16,11)"); // without `toString` static struct Point4 { int x, y; } auto w4 = appender!string(); auto spec4 = singleSpec("%s"); // has to be %s auto p4 = Point4(16,11); formatValue(w4, p4, spec3); assert(w4.data == "Point4(16, 11)");} /// Pointers are formatted as hexadecimal integers.@safe pure unittest{ import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); auto p1 = () @trusted { return cast(void*) 0xFFEECCAA; } (); formatValue(w1, p1, spec1); assert(w1.data == "FFEECCAA"); // null pointers are printed as `"null"` when used with `%s` and as hexadecimal integer else auto w2 = appender!string(); auto spec2 = singleSpec("%s"); auto p2 = () @trusted { return cast(void*) 0x00000000; } (); formatValue(w2, p2, spec2); assert(w2.data == "null"); auto w3 = appender!string(); auto spec3 = singleSpec("%x"); formatValue(w3, p2, spec3); assert(w3.data == "0");} /// SIMD vectors are formatted as arrays.@safe unittest{ import core.simd; // cannot be selective, because float4 might not be defined import std.array : appender; import std.format.spec : singleSpec; auto w = appender!string(); auto spec = singleSpec("%s"); static if (is(float4)) { version (X86) {} else { float4 f4; f4.array[0] = 1; f4.array[1] = 2; f4.array[2] = 3; f4.array[3] = 4; formatValue(w, f4, spec); assert(w.data == "[1, 2, 3, 4]"); } }} import std.format.internal.write; import std.format.spec : FormatSpec;import std.traits : isSomeString; /**Converts its arguments according to a format string and writesthe result to an output range. Lines 1–500 of 1356. Load more
// Written in the D programming language. /** This is a submodule of $(MREF std, format). It provides two functions for writing formatted output: $(LREF formatValue) and $(LREF formattedWrite). The former writes a single value. The latter writes several values at once, interspersed with unformatted text. The following combinations of format characters and types are available: $(BOOKTABLE , $(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o) $(TH x, X) $(TH e, E, f, F, g, G, a, A) $(TH r) $(TH compound)) $(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH))) $(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))) $(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH))) $(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH))) $(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH))) $(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) $(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) $(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes)) $(TR $(TD $(I pointer)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))) $(TR $(TD $(I SIMD vectors)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) $(TR $(TD $(I delegates)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes)) ) Enums can be used with all format characters of the base type. $(H3 $(LNAME2 aggregates, Structs, Unions, Classes, and Interfaces)) Aggregate types can define various `toString` functions. If this function takes a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format, spec) or a $(I format string) as argument, the function decides which format characters are accepted. If no `toString` is defined and the aggregate is an $(REF_ALTTEXT input range, isInputRange, std, range, primitives), it is treated like a range, that is $(B 's'), $(B 'r') and a compound specifier are accepted. In all other cases aggregate types only accept $(B 's'). `toString` should have one of the following signatures: --- void toString(Writer, Char)(ref Writer w, const ref FormatSpec!Char fmt) void toString(Writer)(ref Writer w) string toString(); --- Where `Writer` is an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) which accepts characters $(LPAREN)of type `Char` in the first version$(RPAREN). The template type does not have to be called `Writer`. Sometimes it's not possible to use a template, for example when `toString` overrides `Object.toString`. In this case, the following $(LPAREN)slower and less flexible$(RPAREN) functions can be used: --- void toString(void delegate(const(char)[]) sink, const ref FormatSpec!char fmt); void toString(void delegate(const(char)[]) sink, string fmt); void toString(void delegate(const(char)[]) sink); --- When several of the above `toString` versions are available, the versions with `Writer` take precedence over the versions with a `sink`. `string toString()` has the lowest priority. If none of the above mentioned `toString` versions are available, the aggregates will be formatted by other means, in the following order: If an aggregate is an $(REF_ALTTEXT input range, isInputRange, std, range, primitives), it is formatted like an input range. If an aggregate is a builtin type (using `alias this`), it is formatted like the builtin type. If all else fails, structs are formatted like `Type(field1, field2, ...)`, classes and interfaces are formatted with their fully qualified name and unions with their base name. Copyright: Copyright The D Language Foundation 2000-2013. License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, Andrei Alexandrescu), and Kenji Hara Source: $(PHOBOSSRC std/format/write.d) */ module std.format.write; /** `bool`s are formatted as `"true"` or `"false"` with `%s` and like the `byte`s 1 and 0 with all other format characters. */ @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, true, spec1); assert(w1.data == "true"); auto w2 = appender!string(); auto spec2 = singleSpec("%#x"); formatValue(w2, true, spec2); assert(w2.data == "0x1"); } /// The `null` literal is formatted as `"null"`. @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w = appender!string(); auto spec = singleSpec("%s"); formatValue(w, null, spec); assert(w.data == "null"); } /** Integrals are formatted in (signed) every day notation with `%s` and `%d` and as an (unsigned) image of the underlying bit representation with `%b` (binary), `%u` (decimal), `%o` (octal), and `%x` (hexadecimal). */ @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%d"); formatValue(w1, -1337, spec1); assert(w1.data == "-1337"); auto w2 = appender!string(); auto spec2 = singleSpec("%x"); formatValue(w2, -1337, spec2); assert(w2.data == "fffffac7"); } /** Floating-point values are formatted in natural notation with `%f`, in scientific notation with `%e`, in short notation with `%g`, and in hexadecimal scientific notation with `%a`. If a rounding mode is available, they are rounded according to this rounding mode, otherwise they are rounded to the nearest value, ties to even. */ @safe unittest { import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%.3f"); formatValue(w1, 1337.7779, spec1); assert(w1.data == "1337.778"); auto w2 = appender!string(); auto spec2 = singleSpec("%.3e"); formatValue(w2, 1337.7779, spec2); assert(w2.data == "1.338e+03"); auto w3 = appender!string(); auto spec3 = singleSpec("%.3g"); formatValue(w3, 1337.7779, spec3); assert(w3.data == "1.34e+03"); auto w4 = appender!string(); auto spec4 = singleSpec("%.3a"); formatValue(w4, 1337.7779, spec4); assert(w4.data == "0x1.4e7p+10"); } /** Individual characters (`char`, `wchar`, or `dchar`) are formatted as Unicode characters with `%s` and `%c` and as integers (`ubyte`, `ushort`, `uint`) with all other format characters. With $(MREF_ALTTEXT compound specifiers, std,format) characters are treated differently. */ @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%c"); formatValue(w1, 'ì', spec1); assert(w1.data == "ì"); auto w2 = appender!string(); auto spec2 = singleSpec("%#x"); formatValue(w2, 'ì', spec2); assert(w2.data == "0xec"); } /** Strings are formatted as a sequence of characters with `%s`. Non-printable characters are not escaped. With a compound specifier the string is treated like a range of characters. With $(MREF_ALTTEXT compound specifiers, std,format) strings are treated differently. */ @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, "hello", spec1); assert(w1.data == "hello"); auto w2 = appender!string(); auto spec2 = singleSpec("%(%#x%|/%)"); formatValue(w2, "hello", spec2); assert(w2.data == "0x68/0x65/0x6c/0x6c/0x6f"); } /// Static arrays are formatted as dynamic arrays. @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w = appender!string(); auto spec = singleSpec("%s"); int[2] two = [1, 2]; formatValue(w, two, spec); assert(w.data == "[1, 2]"); } /** Dynamic arrays are formatted as input ranges. */ @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); auto two = [1, 2]; formatValue(w1, two, spec1); assert(w1.data == "[1, 2]"); auto w2 = appender!string(); auto spec2 = singleSpec("%(%g%|, %)"); auto consts = [3.1415926, 299792458, 6.67430e-11]; formatValue(w2, consts, spec2); assert(w2.data == "3.14159, 2.99792e+08, 6.6743e-11"); // void[] is treated like ubyte[] auto w3 = appender!string(); auto spec3 = singleSpec("%s"); void[] val = cast(void[]) cast(ubyte[])[1, 2, 3]; formatValue(w3, val, spec3); assert(w3.data == "[1, 2, 3]"); } /** Associative arrays are formatted by using `':'` and `", "` as separators, enclosed by `'['` and `']'` when used with `%s`. It's also possible to use a compound specifier for better control. Please note, that the order of the elements is not defined, therefore the result of this function might differ. */ @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto aa = [10:17.5, 20:9.99]; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, aa, spec1); assert(w1.data == "[10:17.5, 20:9.99]" || w1.data == "[20:9.99, 10:17.5]"); auto w2 = appender!string(); auto spec2 = singleSpec("%(%x = %.0e%| # %)"); formatValue(w2, aa, spec2); assert(w2.data == "a = 2e+01 # 14 = 1e+01" || w2.data == "14 = 1e+01 # a = 2e+01"); } /** `enum`s are formatted as their name when used with `%s` and like their base value else. */ @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; enum A { first, second, third } auto w1 = appender!string(); auto spec1 = singleSpec("%s"); formatValue(w1, A.second, spec1); assert(w1.data == "second"); auto w2 = appender!string(); auto spec2 = singleSpec("%d"); formatValue(w2, A.second, spec2); assert(w2.data == "1"); // values of an enum that have no name are formatted with %s using a cast A a = A.third; a++; auto w3 = appender!string(); auto spec3 = singleSpec("%s"); formatValue(w3, a, spec3); assert(w3.data == "cast(A)3"); } /** `structs`, `unions`, `classes` and `interfaces` can be formatted in several different ways. The following example highlights `struct` formatting, however, it applies to other aggregates as well. */ @safe unittest { import std.array : appender; import std.format.spec : FormatSpec, singleSpec; // Using a `toString` with a writer static struct Point1 { import std.range.primitives : isOutputRange, put; int x, y; void toString(W)(ref W writer, scope const ref FormatSpec!char f) if (isOutputRange!(W, char)) { put(writer, "("); formatValue(writer, x, f); put(writer, ","); formatValue(writer, y, f); put(writer, ")"); } } auto w1 = appender!string(); auto spec1 = singleSpec("%s"); auto p1 = Point1(16, 11); formatValue(w1, p1, spec1); assert(w1.data == "(16,11)"); // Using a `toString` with a sink static struct Point2 { int x, y; void toString(scope void delegate(scope const(char)[]) @safe sink, scope const FormatSpec!char fmt) const { sink("("); sink.formatValue(x, fmt); sink(","); sink.formatValue(y, fmt); sink(")"); } } auto w2 = appender!string(); auto spec2 = singleSpec("%03d"); auto p2 = Point2(16,11); formatValue(w2, p2, spec2); assert(w2.data == "(016,011)"); // Using `string toString()` static struct Point3 { int x, y; string toString() { import std.conv : to; return "(" ~ to!string(x) ~ "," ~ to!string(y) ~ ")"; } } auto w3 = appender!string(); auto spec3 = singleSpec("%s"); // has to be %s auto p3 = Point3(16,11); formatValue(w3, p3, spec3); assert(w3.data == "(16,11)"); // without `toString` static struct Point4 { int x, y; } auto w4 = appender!string(); auto spec4 = singleSpec("%s"); // has to be %s auto p4 = Point4(16,11); formatValue(w4, p4, spec3); assert(w4.data == "Point4(16, 11)"); } /// Pointers are formatted as hexadecimal integers. @safe pure unittest { import std.array : appender; import std.format.spec : singleSpec; auto w1 = appender!string(); auto spec1 = singleSpec("%s"); auto p1 = () @trusted { return cast(void*) 0xFFEECCAA; } (); formatValue(w1, p1, spec1); assert(w1.data == "FFEECCAA"); // null pointers are printed as `"null"` when used with `%s` and as hexadecimal integer else auto w2 = appender!string(); auto spec2 = singleSpec("%s"); auto p2 = () @trusted { return cast(void*) 0x00000000; } (); formatValue(w2, p2, spec2); assert(w2.data == "null"); auto w3 = appender!string(); auto spec3 = singleSpec("%x"); formatValue(w3, p2, spec3); assert(w3.data == "0"); } /// SIMD vectors are formatted as arrays. @safe unittest { import core.simd; // cannot be selective, because float4 might not be defined import std.array : appender; import std.format.spec : singleSpec; auto w = appender!string(); auto spec = singleSpec("%s"); static if (is(float4)) { version (X86) {} else { float4 f4; f4.array[0] = 1; f4.array[1] = 2; f4.array[2] = 3; f4.array[3] = 4; formatValue(w, f4, spec); assert(w.data == "[1, 2, 3, 4]"); } } } import std.format.internal.write; import std.format.spec : FormatSpec; import std.traits : isSomeString; /** Converts its arguments according to a format string and writes the result to an output range.