707 lines
33 KiB
Markdown
707 lines
33 KiB
Markdown
[format.formatter]
|
||
|
||
# 28 Text processing library [[text]](./#text)
|
||
|
||
## 28.5 Formatting [[format]](format#formatter)
|
||
|
||
### 28.5.6 Formatter [format.formatter]
|
||
|
||
#### [28.5.6.1](#formatter.requirements) Formatter requirements [[formatter.requirements]](formatter.requirements)
|
||
|
||
[1](#formatter.requirements-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L6981)
|
||
|
||
A type F meets the *BasicFormatter* requirements if
|
||
it meets the
|
||
|
||
- [(1.1)](#formatter.requirements-1.1)
|
||
|
||
*Cpp17DefaultConstructible* (Table [30](utility.arg.requirements#tab:cpp17.defaultconstructible "Table 30: Cpp17DefaultConstructible requirements")),
|
||
|
||
- [(1.2)](#formatter.requirements-1.2)
|
||
|
||
*Cpp17CopyConstructible* (Table [32](utility.arg.requirements#tab:cpp17.copyconstructible "Table 32: Cpp17CopyConstructible requirements (in addition to Cpp17MoveConstructible)")),
|
||
|
||
- [(1.3)](#formatter.requirements-1.3)
|
||
|
||
*Cpp17CopyAssignable* (Table [34](utility.arg.requirements#tab:cpp17.copyassignable "Table 34: Cpp17CopyAssignable requirements (in addition to Cpp17MoveAssignable)")),
|
||
|
||
- [(1.4)](#formatter.requirements-1.4)
|
||
|
||
*Cpp17Swappable* ([[swappable.requirements]](swappable.requirements "16.4.4.3 Swappable requirements")), and
|
||
|
||
- [(1.5)](#formatter.requirements-1.5)
|
||
|
||
*Cpp17Destructible* (Table [35](utility.arg.requirements#tab:cpp17.destructible "Table 35: Cpp17Destructible requirements"))
|
||
|
||
requirements, and
|
||
the expressions shown in Table [112](#tab:formatter.basic "Table 112: BasicFormatter requirements") are valid and
|
||
have the indicated semantics[.](#formatter.requirements-1.sentence-1)
|
||
|
||
[2](#formatter.requirements-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L6995)
|
||
|
||
A type F meets the *Formatter* requirements
|
||
if it meets the *BasicFormatter* requirements and
|
||
the expressions shown in Table [113](#tab:formatter "Table 113: Formatter requirements") are valid and
|
||
have the indicated semantics[.](#formatter.requirements-2.sentence-1)
|
||
|
||
[3](#formatter.requirements-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7001)
|
||
|
||
Given character type charT, output iterator typeOut, and formatting argument type T,
|
||
in Table [112](#tab:formatter.basic "Table 112: BasicFormatter requirements") and Table [113](#tab:formatter "Table 113: Formatter requirements"):
|
||
|
||
- [(3.1)](#formatter.requirements-3.1)
|
||
|
||
f is a value of type (possibly const) F,
|
||
|
||
- [(3.2)](#formatter.requirements-3.2)
|
||
|
||
g is an lvalue of type F,
|
||
|
||
- [(3.3)](#formatter.requirements-3.3)
|
||
|
||
u is an lvalue of type T,
|
||
|
||
- [(3.4)](#formatter.requirements-3.4)
|
||
|
||
t is a value of a type convertible to (possibly const) T,
|
||
|
||
- [(3.5)](#formatter.requirements-3.5)
|
||
|
||
PC is basic_format_parse_context<charT>,
|
||
|
||
- [(3.6)](#formatter.requirements-3.6)
|
||
|
||
FC is basic_format_context<Out, charT>,
|
||
|
||
- [(3.7)](#formatter.requirements-3.7)
|
||
|
||
pc is an lvalue of type PC, and
|
||
|
||
- [(3.8)](#formatter.requirements-3.8)
|
||
|
||
fc is an lvalue of type FC[.](#formatter.requirements-3.sentence-1)
|
||
|
||
pc.begin() points to the beginning of the*format-spec* ([[format.string]](format.string "28.5.2 Format string"))
|
||
of the replacement field being formatted
|
||
in the format string[.](#formatter.requirements-3.sentence-2)
|
||
|
||
If *format-spec* is not present or empty then eitherpc.begin() == pc.end() or*pc.begin() == '}'[.](#formatter.requirements-3.sentence-3)
|
||
|
||
Table [112](#tab:formatter.basic) — *BasicFormatter* requirements [[tab:formatter.basic]](./tab:formatter.basic)
|
||
|
||
| [ð](#tab:formatter.basic-row-1)<br>**Expression** | **Return type** | **Requirement** |
|
||
| --- | --- | --- |
|
||
| [ð](#tab:formatter.basic-row-2)<br>g.parse(pc) | PC::iterator | Parses *format-spec* ([[format.string]](format.string "28.5.2 Format string")) for type T in the range [pc.begin(), pc.end()) until the first unmatched character[.](#tab:formatter.basic-row-2-column-3-sentence-1)<br>Throws format_error unless the whole range is parsed or the unmatched character is }[.](#tab:formatter.basic-row-2-column-3-sentence-2)<br>[*Note [1](#tab:formatter.basic-row-2-column-3-note-1)*:<br>This allows formatters to emit meaningful error messages[.](#tab:formatter.basic-row-2-column-3-sentence-3) â *end note*]<br> Stores the parsed format specifiers in *this and returns an iterator past the end of the parsed range[.](#tab:formatter.basic-row-2-column-3-sentence-4) |
|
||
| [ð](#tab:formatter.basic-row-3)<br>f.format(u, fc) | FC::iterator | Formats u according to the specifiers stored in *this, writes the output to fc.out(), and returns an iterator past the end of the output range[.](#tab:formatter.basic-row-3-column-3-sentence-1)<br>The output shall only depend on u, fc.locale(), fc.arg(n) for any value n of type size_t, and the range [pc.begin(), pc.end()) from the last call to f.parse(pc)[.](#tab:formatter.basic-row-3-column-3-sentence-2) |
|
||
|
||
Table [113](#tab:formatter) — *Formatter* requirements [[tab:formatter]](./tab:formatter)
|
||
|
||
| [ð](#tab:formatter-row-1)<br>**Expression** | **Return type** | **Requirement** |
|
||
| --- | --- | --- |
|
||
| [ð](#tab:formatter-row-2)<br>f.format(t, fc) | FC::iterator | Formats t according to the specifiers stored in *this, writes the output to fc.out(), and returns an iterator past the end of the output range[.](#tab:formatter-row-2-column-3-sentence-1)<br>The output shall only depend on t, fc.locale(), fc.arg(n) for any value n of type size_t, and the range [pc.begin(), pc.end()) from the last call to f.parse(pc)[.](#tab:formatter-row-2-column-3-sentence-2) |
|
||
| [ð](#tab:formatter-row-3)<br>f.format(u, fc) | FC::iterator | As above, but does not modify u[.](#tab:formatter-row-3-column-3-sentence-1) |
|
||
|
||
#### [28.5.6.2](#locking) Formatter locking [[format.formatter.locking]](format.formatter.locking)
|
||
|
||
[ð](#lib:enable_nonlocking_formatter_optimization)
|
||
|
||
`template<class T>
|
||
constexpr bool enable_nonlocking_formatter_optimization = false;
|
||
`
|
||
|
||
[1](#locking-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7086)
|
||
|
||
*Remarks*: Pursuant to [[namespace.std]](namespace.std "16.4.5.2.1 Namespace std"),
|
||
users may specialize enable_nonlocking_formatter_optimization for
|
||
cv-unqualified program-defined types[.](#locking-1.sentence-1)
|
||
|
||
Such specializations shall be usable in constant expressions ([[expr.const]](expr.const "7.7 Constant expressions"))
|
||
and have type const bool[.](#locking-1.sentence-2)
|
||
|
||
#### [28.5.6.3](#format.formattable) Concept formattable [[format.formattable]](format.formattable)
|
||
|
||
[1](#format.formattable-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7097)
|
||
|
||
Let *fmt-iter-for*<charT> be an unspecified type
|
||
that models[output_iterator](iterator.concept.output#concept:output_iterator "24.3.4.10 Concept output_iterator [iterator.concept.output]")<const charT&> ([[iterator.concept.output]](iterator.concept.output "24.3.4.10 Concept output_iterator"))[.](#format.formattable-1.sentence-1)
|
||
|
||
template<class T, class Context, class Formatter = typename Context::template formatter_type<remove_const_t<T>>>concept [*formattable-with*](#concept:formattable-with "28.5.6.3 Concept formattable [format.formattable]") = // *exposition only*[semiregular](concepts.object#concept:semiregular "18.6 Object concepts [concepts.object]")<Formatter> &&requires(Formatter& f, const Formatter& cf, T&& t, Context fc,
|
||
basic_format_parse_context<typename Context::char_type> pc){{ f.parse(pc) } -> [same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<typename decltype(pc)::iterator>; { cf.format(t, fc) } -> [same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<typename Context::iterator>; };
|
||
|
||
template<class T, class charT>concept [formattable](#concept:formattable "28.5.6.3 Concept formattable [format.formattable]") =[*formattable-with*](#concept:formattable-with "28.5.6.3 Concept formattable [format.formattable]")<remove_reference_t<T>, basic_format_context<*fmt-iter-for*<charT>, charT>>;
|
||
|
||
[2](#format.formattable-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7119)
|
||
|
||
A type T and a character type charT model [formattable](#concept:formattable "28.5.6.3 Concept formattable [format.formattable]") if formatter<remove_cvref_t<T>, charT> meets
|
||
the *BasicFormatter* requirements ([[formatter.requirements]](#formatter.requirements "28.5.6.1 Formatter requirements"))
|
||
and, if remove_reference_t<T> is const-qualified,
|
||
the *Formatter* requirements[.](#format.formattable-2.sentence-1)
|
||
|
||
#### [28.5.6.4](#spec) Formatter specializations [[format.formatter.spec]](format.formatter.spec)
|
||
|
||
[1](#spec-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7130)
|
||
|
||
The functions defined in [[format.functions]](format.functions "28.5.5 Formatting functions") use
|
||
specializations of the class template formatter to format
|
||
individual arguments[.](#spec-1.sentence-1)
|
||
|
||
[2](#spec-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7136)
|
||
|
||
Let charT be either char or wchar_t[.](#spec-2.sentence-1)
|
||
|
||
Each specialization of formatter is either enabled or disabled,
|
||
as described below[.](#spec-2.sentence-2)
|
||
|
||
A [*debug-enabled*](#def:debug-enabled "28.5.6.4 Formatter specializations [format.formatter.spec]") specialization of formatter additionally provides
|
||
a public, constexpr, non-static member function set_debug_format() which modifies the state of the formatter to be as if
|
||
the type of the *std-format-spec* parsed by the last call to parse were ?[.](#spec-2.sentence-3)
|
||
|
||
Each header that declares the template formatter provides the following enabled specializations:
|
||
|
||
- [(2.1)](#spec-2.1)
|
||
|
||
The debug-enabled specializationstemplate<> struct formatter<char, char>;template<> struct formatter<char, wchar_t>;template<> struct formatter<wchar_t, wchar_t>;
|
||
|
||
- [(2.2)](#spec-2.2)
|
||
|
||
For each charT,
|
||
the debug-enabled string type specializationstemplate<> struct formatter<charT*, charT>;template<> struct formatter<const charT*, charT>;template<size_t N> struct formatter<charT[N], charT>;template<class traits, class Allocator>struct formatter<basic_string<charT, traits, Allocator>, charT>;template<class traits>struct formatter<basic_string_view<charT, traits>, charT>;
|
||
|
||
- [(2.3)](#spec-2.3)
|
||
|
||
For each charT,
|
||
for each cv-unqualified arithmetic type ArithmeticT other thanchar,wchar_t,char8_t,char16_t, orchar32_t,
|
||
a specializationtemplate<> struct formatter<ArithmeticT, charT>;
|
||
|
||
- [(2.4)](#spec-2.4)
|
||
|
||
For each charT,
|
||
the pointer type specializationstemplate<> struct formatter<nullptr_t, charT>;template<> struct formatter<void*, charT>;template<> struct formatter<const void*, charT>;
|
||
|
||
The parse member functions of these formatters
|
||
interpret the format specification
|
||
as a *std-format-spec* as described in [[format.string.std]](format.string.std "28.5.2.2 Standard format specifiers")[.](#spec-2.sentence-4)
|
||
|
||
[3](#spec-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7204)
|
||
|
||
Unless specified otherwise, for each type T for which
|
||
a formatter specialization is provided by the library,
|
||
each of the headers provides the following specialization:template<> inline constexpr bool enable_nonlocking_formatter_optimization<T> = true;
|
||
|
||
[*Note [1](#spec-note-1)*:
|
||
|
||
Specializations such as formatter<wchar_t, char> that would require implicit
|
||
multibyte / wide string or character conversion are disabled[.](#spec-3.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
[4](#spec-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7217)
|
||
|
||
The header <format> provides
|
||
the following disabled specializations:
|
||
|
||
- [(4.1)](#spec-4.1)
|
||
|
||
The string type specializationstemplate<> struct formatter<char*, wchar_t>;template<> struct formatter<const char*, wchar_t>;template<size_t N> struct formatter<char[N], wchar_t>;template<class traits, class Allocator>struct formatter<basic_string<char, traits, Allocator>, wchar_t>;template<class traits>struct formatter<basic_string_view<char, traits>, wchar_t>;
|
||
|
||
[5](#spec-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7234)
|
||
|
||
For any types T and charT for which
|
||
neither the library nor the user provides
|
||
an explicit or partial specialization of
|
||
the class template formatter,formatter<T, charT> is disabled[.](#spec-5.sentence-1)
|
||
|
||
[6](#spec-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7241)
|
||
|
||
If the library provides an explicit or partial specialization offormatter<T, charT>, that specialization is enabled
|
||
and meets the *Formatter* requirements
|
||
except as noted otherwise[.](#spec-6.sentence-1)
|
||
|
||
[7](#spec-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7247)
|
||
|
||
If F is a disabled specialization of formatter, these
|
||
values are false:
|
||
|
||
- [(7.1)](#spec-7.1)
|
||
|
||
is_default_constructible_v<F>,
|
||
|
||
- [(7.2)](#spec-7.2)
|
||
|
||
is_copy_constructible_v<F>,
|
||
|
||
- [(7.3)](#spec-7.3)
|
||
|
||
is_move_constructible_v<F>,
|
||
|
||
- [(7.4)](#spec-7.4)
|
||
|
||
is_copy_assignable_v<F>, and
|
||
|
||
- [(7.5)](#spec-7.5)
|
||
|
||
is_move_assignable_v<F>[.](#spec-7.sentence-1)
|
||
|
||
[8](#spec-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7258)
|
||
|
||
An enabled specialization formatter<T, charT> meets the*BasicFormatter* requirements ([[formatter.requirements]](#formatter.requirements "28.5.6.1 Formatter requirements"))[.](#spec-8.sentence-1)
|
||
|
||
[*Example [1](#spec-example-1)*: #include <format>#include <string>enum color { red, green, blue };const char* color_names[] = { "red", "green", "blue" };
|
||
|
||
template<> struct std::formatter<color> : std::formatter<const char*> {auto format(color c, format_context& ctx) const {return formatter<const char*>::format(color_names[c], ctx); }};
|
||
|
||
struct err {};
|
||
|
||
std::string s0 = std::format("{}", 42); // OK, library-provided formatter std::string s1 = std::format("{}", L"foo"); // error: disabled formatter std::string s2 = std::format("{}", red); // OK, user-provided formatter std::string s3 = std::format("{}", err{}); // error: disabled formatter â *end example*]
|
||
|
||
#### [28.5.6.5](#format.string.escaped) Formatting escaped characters and strings [[format.string.escaped]](format.string.escaped)
|
||
|
||
[1](#format.string.escaped-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7286)
|
||
|
||
A character or string can be formatted as [*escaped*](#def:escaped "28.5.6.5 Formatting escaped characters and strings [format.string.escaped]") to make it more suitable for debugging or for logging[.](#format.string.escaped-1.sentence-1)
|
||
|
||
[2](#format.string.escaped-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7292)
|
||
|
||
The escaped string *E* representation of a string *S* is constructed by encoding a sequence of characters as follows[.](#format.string.escaped-2.sentence-1)
|
||
|
||
The associated character encoding *CE* for charT (Table [12](lex.string#tab:lex.string.literal "Table 12: String literals"))
|
||
is used to both interpret *S* and construct *E*[.](#format.string.escaped-2.sentence-2)
|
||
|
||
- [(2.1)](#format.string.escaped-2.1)
|
||
|
||
U+0022 quotation mark (") is appended to *E*[.](#format.string.escaped-2.1.sentence-1)
|
||
|
||
- [(2.2)](#format.string.escaped-2.2)
|
||
|
||
For each code unit sequence *X* in *S* that either
|
||
encodes a single character,
|
||
is a shift sequence, or
|
||
is a sequence of ill-formed code units,
|
||
processing is in order as follows:
|
||
|
||
* [(2.2.1)](#format.string.escaped-2.2.1)
|
||
|
||
If *X* encodes a single character *C*, then:
|
||
|
||
+
|
||
[(2.2.1.1)](#format.string.escaped-2.2.1.1)
|
||
If *C* is one of the characters in Table [114](#tab:format.escape.sequences "Table 114: Mapping of characters to escape sequences"),
|
||
then the two characters shown as the corresponding escape sequence
|
||
are appended to *E*[.](#format.string.escaped-2.2.1.1.sentence-1)
|
||
|
||
+
|
||
[(2.2.1.2)](#format.string.escaped-2.2.1.2)
|
||
Otherwise, if *C* is not U+0020 space and
|
||
|
||
- [(2.2.1.2.1)](#format.string.escaped-2.2.1.2.1)
|
||
|
||
*CE* is UTF-8, UTF-16, or UTF-32 and*C* corresponds to a Unicode scalar value
|
||
whose Unicode property General_Category has a value in the groupsSeparator (Z) or Other (C),
|
||
as described by UAX #44 of the Unicode Standard, or
|
||
|
||
- [(2.2.1.2.2)](#format.string.escaped-2.2.1.2.2)
|
||
|
||
*CE* is UTF-8, UTF-16, or UTF-32 and*C* corresponds to a Unicode scalar value
|
||
with the Unicode property Grapheme_Extend=Yes as described by UAX #44 of the Unicode Standard and*C* is not immediately preceded in *S* by
|
||
a character *P* appended to *E* without translation to an escape sequence, or
|
||
|
||
- [(2.2.1.2.3)](#format.string.escaped-2.2.1.2.3)
|
||
|
||
*CE* is neither UTF-8, UTF-16, nor UTF-32 and*C* is one of an implementation-defined set
|
||
of separator or non-printable characters
|
||
|
||
then the sequence \u{*hex-digit-sequence*} is appended to *E*,
|
||
where *hex-digit-sequence* is the shortest hexadecimal representation
|
||
of *C* using lower-case hexadecimal digits[.](#format.string.escaped-2.2.1.2.sentence-2)
|
||
|
||
+
|
||
[(2.2.1.3)](#format.string.escaped-2.2.1.3)
|
||
Otherwise, *C* is appended to *E*[.](#format.string.escaped-2.2.1.3.sentence-1)
|
||
|
||
* [(2.2.2)](#format.string.escaped-2.2.2)
|
||
|
||
Otherwise, if *X* is a shift sequence,
|
||
the effect on *E* and further decoding of *S* is unspecified[.](#format.string.escaped-2.2.2.sentence-1)
|
||
*Recommended practice*: A shift sequence should be represented in *E* such that the original code unit sequence of *S* can be reconstructed[.](#format.string.escaped-2.2.2.sentence-2)
|
||
|
||
* [(2.2.3)](#format.string.escaped-2.2.3)
|
||
|
||
Otherwise (*X* is a sequence of ill-formed code units),
|
||
each code unit *U* is appended to *E* in order
|
||
as the sequence \x{*hex-digit-sequence*},
|
||
where *hex-digit-sequence* is the shortest hexadecimal representation of *U* using lower-case hexadecimal digits[.](#format.string.escaped-2.2.3.sentence-1)
|
||
|
||
- [(2.3)](#format.string.escaped-2.3)
|
||
|
||
Finally, U+0022 quotation mark (")
|
||
is appended to *E*[.](#format.string.escaped-2.3.sentence-1)
|
||
|
||
Table [114](#tab:format.escape.sequences) — Mapping of characters to escape sequences [[tab:format.escape.sequences]](./tab:format.escape.sequences)
|
||
|
||
| [ð](#tab:format.escape.sequences-row-1)<br>**Character** | **Escape sequence** |
|
||
| --- | --- |
|
||
| [ð](#tab:format.escape.sequences-row-2)<br>U+0009 character tabulation | \t |
|
||
| [ð](#tab:format.escape.sequences-row-3)<br>U+000a line feed | \n |
|
||
| [ð](#tab:format.escape.sequences-row-4)<br>U+000d carriage return | \r |
|
||
| [ð](#tab:format.escape.sequences-row-5)<br>U+0022 quotation mark | \" |
|
||
| [ð](#tab:format.escape.sequences-row-6)<br>U+005c reverse solidus | \\ |
|
||
|
||
[3](#format.string.escaped-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7404)
|
||
|
||
The escaped string representation of a character *C* is equivalent to the escaped string representation
|
||
of a string of *C*, except that:
|
||
|
||
- [(3.1)](#format.string.escaped-3.1)
|
||
|
||
the result starts and ends with U+0027 apostrophe (')
|
||
instead of U+0022 quotation mark ("), and
|
||
|
||
- [(3.2)](#format.string.escaped-3.2)
|
||
|
||
if *C* is U+0027 apostrophe,
|
||
the two characters \' are appended to *E*, and
|
||
|
||
- [(3.3)](#format.string.escaped-3.3)
|
||
|
||
if *C* is U+0022 quotation mark,
|
||
then *C* is appended unchanged.
|
||
|
||
[*Example [1](#format.string.escaped-example-1)*: string s0 = format("[{}]", "h\tllo"); // s0 has value: [h llo] string s1 = format("[{:?}]", "h\tllo"); // s1 has value: ["h\tllo"] string s2 = format("[{:?}]", "СпаÑибо, ÐикÑÐ¾Ñ â¥!"); // s2 has value: ["СпаÑибо, ÐикÑÐ¾Ñ â¥!"] string s3 = format("[{:?}, {:?}]", '\'', '"'); // s3 has value: ['\'', '"']// The following examples assume use of the UTF-8 encoding string s4 = format("[{:?}]", string("\0 \n \t \x02 \x1b", 9)); // s4 has value: ["\u{0} \n \t \u{2} \u{1b}"] string s5 = format("[{:?}]", "\xc3\x28"); // invalid UTF-8, s5 has value: ["\x{c3}("] string s6 = format("[{:?}]", "ð¤·ð»ââï¸"); // s6 has value: ["ð¤·\u{200d}â"] string s7 = format("[{:?}]", "\u0301"); // s7 has value: ["\u{301}"] string s8 = format("[{:?}]", "\\\u0301"); // s8 has value: ["\\\u{301}"] string s9 = format("[{:?}]", "e\u0301\u0323"); // s9 has value: ["eÌÌ£"] â *end example*]
|
||
|
||
#### [28.5.6.6](#format.parse.ctx) Class template basic_format_parse_context [[format.parse.ctx]](format.parse.ctx)
|
||
|
||
[ð](#lib:basic_format_parse_context)
|
||
|
||
namespace std {template<class charT>class basic_format_parse_context {public:using char_type = charT; using const_iterator = typename basic_string_view<charT>::const_iterator; using iterator = const_iterator; private: iterator begin_; // *exposition only* iterator end_; // *exposition only*enum indexing { unknown, manual, automatic }; // *exposition only* indexing indexing_; // *exposition only* size_t next_arg_id_; // *exposition only* size_t num_args_; // *exposition only*public:constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt) noexcept;
|
||
basic_format_parse_context(const basic_format_parse_context&) = delete;
|
||
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete; constexpr const_iterator begin() const noexcept; constexpr const_iterator end() const noexcept; constexpr void advance_to(const_iterator it); constexpr size_t next_arg_id(); constexpr void check_arg_id(size_t id); template<class... Ts>constexpr void check_dynamic_spec(size_t id) noexcept; constexpr void check_dynamic_spec_integral(size_t id) noexcept; constexpr void check_dynamic_spec_string(size_t id) noexcept; };}
|
||
|
||
[1](#format.parse.ctx-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7482)
|
||
|
||
An instance of basic_format_parse_context holds
|
||
the format string parsing state, consisting of
|
||
the format string range being parsed and
|
||
the argument counter for automatic indexing[.](#format.parse.ctx-1.sentence-1)
|
||
|
||
[2](#format.parse.ctx-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7488)
|
||
|
||
If a program declares an explicit or partial specialization ofbasic_format_parse_context,
|
||
the program is ill-formed, no diagnostic required[.](#format.parse.ctx-2.sentence-1)
|
||
|
||
[ð](#lib:basic_format_parse_context,constructor)
|
||
|
||
`constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt) noexcept;
|
||
`
|
||
|
||
[3](#format.parse.ctx-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7499)
|
||
|
||
*Effects*: Initializesbegin_ with fmt.begin(),end_ with fmt.end(),indexing_ with unknown,next_arg_id_ with 0, andnum_args_ with 0[.](#format.parse.ctx-3.sentence-1)
|
||
|
||
[*Note [1](#format.parse.ctx-note-1)*:
|
||
|
||
Any call tonext_arg_id, check_arg_id, or check_dynamic_spec on an instance of basic_format_parse_context initialized using this constructor is not a core constant expression[.](#format.parse.ctx-3.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[ð](#lib:begin,basic_format_parse_context)
|
||
|
||
`constexpr const_iterator begin() const noexcept;
|
||
`
|
||
|
||
[4](#format.parse.ctx-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7521)
|
||
|
||
*Returns*: begin_[.](#format.parse.ctx-4.sentence-1)
|
||
|
||
[ð](#lib:end,basic_format_parse_context)
|
||
|
||
`constexpr const_iterator end() const noexcept;
|
||
`
|
||
|
||
[5](#format.parse.ctx-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7532)
|
||
|
||
*Returns*: end_[.](#format.parse.ctx-5.sentence-1)
|
||
|
||
[ð](#lib:advance_to,basic_format_parse_context)
|
||
|
||
`constexpr void advance_to(const_iterator it);
|
||
`
|
||
|
||
[6](#format.parse.ctx-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7543)
|
||
|
||
*Preconditions*: end() is reachable from it[.](#format.parse.ctx-6.sentence-1)
|
||
|
||
[7](#format.parse.ctx-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7547)
|
||
|
||
*Effects*: Equivalent to: begin_ = it;
|
||
|
||
[ð](#lib:next_arg_id,basic_format_parse_context)
|
||
|
||
`constexpr size_t next_arg_id();
|
||
`
|
||
|
||
[8](#format.parse.ctx-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7558)
|
||
|
||
*Effects*: If indexing_ != manual is true, equivalent to:if (indexing_ == unknown) indexing_ = automatic;return next_arg_id_++;
|
||
|
||
[9](#format.parse.ctx-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7567)
|
||
|
||
*Throws*: format_error if indexing_ == manual is true[.](#format.parse.ctx-9.sentence-1)
|
||
|
||
[*Note [2](#format.parse.ctx-note-2)*:
|
||
|
||
This indicates mixing of automatic and manual argument indexing[.](#format.parse.ctx-9.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[10](#format.parse.ctx-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7574)
|
||
|
||
*Remarks*: Let *cur-arg-id* be the value of next_arg_id_ prior to this call[.](#format.parse.ctx-10.sentence-1)
|
||
|
||
Call expressions where *cur-arg-id* >= num_args_ is true are not core constant expressions ([[expr.const]](expr.const "7.7 Constant expressions"))[.](#format.parse.ctx-10.sentence-2)
|
||
|
||
[ð](#lib:check_arg_id,basic_format_parse_context)
|
||
|
||
`constexpr void check_arg_id(size_t id);
|
||
`
|
||
|
||
[11](#format.parse.ctx-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7587)
|
||
|
||
*Effects*: If indexing_ != automatic is true, equivalent to:if (indexing_ == unknown) indexing_ = manual;
|
||
|
||
[12](#format.parse.ctx-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7595)
|
||
|
||
*Throws*: format_error ifindexing_ == automatic is true[.](#format.parse.ctx-12.sentence-1)
|
||
|
||
[*Note [3](#format.parse.ctx-note-3)*:
|
||
|
||
This indicates mixing of automatic and manual argument indexing[.](#format.parse.ctx-12.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[13](#format.parse.ctx-13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7603)
|
||
|
||
*Remarks*: A call to this function is a core constant expression ([[expr.const]](expr.const "7.7 Constant expressions")) only ifid < num_args_ is true[.](#format.parse.ctx-13.sentence-1)
|
||
|
||
[ð](#lib:check_dynamic_spec,basic_format_parse_context)
|
||
|
||
`template<class... Ts>
|
||
constexpr void check_dynamic_spec(size_t id) noexcept;
|
||
`
|
||
|
||
[14](#format.parse.ctx-14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7616)
|
||
|
||
*Mandates*: sizeof...(Ts) ⥠1[.](#format.parse.ctx-14.sentence-1)
|
||
|
||
The types in Ts... are unique[.](#format.parse.ctx-14.sentence-2)
|
||
|
||
Each type in Ts... is one ofbool,char_type,int,unsigned int,long long int,unsigned long long int,float,double,long double,const char_type*,basic_string_view<char_type>, orconst void*[.](#format.parse.ctx-14.sentence-3)
|
||
|
||
[15](#format.parse.ctx-15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7634)
|
||
|
||
*Remarks*: A call to this function is a core constant expression only if
|
||
|
||
- [(15.1)](#format.parse.ctx-15.1)
|
||
|
||
id < num_args_ is true and
|
||
|
||
- [(15.2)](#format.parse.ctx-15.2)
|
||
|
||
the type of the corresponding format argument
|
||
(after conversion to basic_format_arg<Context>) is one of the types in Ts...[.](#format.parse.ctx-15.sentence-1)
|
||
|
||
[ð](#lib:check_dynamic_spec_integral,basic_format_parse_context)
|
||
|
||
`constexpr void check_dynamic_spec_integral(size_t id) noexcept;
|
||
`
|
||
|
||
[16](#format.parse.ctx-16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7652)
|
||
|
||
*Effects*: Equivalent to:check_dynamic_spec<int, unsigned int, long long int, unsigned long long int>(id);
|
||
|
||
[ð](#lib:check_dynamic_spec_string,basic_format_parse_context)
|
||
|
||
`constexpr void check_dynamic_spec_string(size_t id) noexcept;
|
||
`
|
||
|
||
[17](#format.parse.ctx-17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7666)
|
||
|
||
*Effects*: Equivalent to:check_dynamic_spec<const char_type*, basic_string_view<char_type>>(id);
|
||
|
||
#### [28.5.6.7](#format.context) Class template basic_format_context [[format.context]](format.context)
|
||
|
||
[ð](#lib:basic_format_context)
|
||
|
||
namespace std {template<class Out, class charT>class basic_format_context { basic_format_args<basic_format_context> args_; // *exposition only* Out out_; // *exposition only* basic_format_context(const basic_format_context&) = delete;
|
||
basic_format_context& operator=(const basic_format_context&) = delete; public:using iterator = Out; using char_type = charT; template<class T> using formatter_type = formatter<T, charT>;
|
||
|
||
basic_format_arg<basic_format_context> arg(size_t id) const noexcept;
|
||
std::locale locale();
|
||
|
||
iterator out(); void advance_to(iterator it); };}
|
||
|
||
[1](#format.context-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7704)
|
||
|
||
An instance of basic_format_context holds formatting state
|
||
consisting of the formatting arguments and the output iterator[.](#format.context-1.sentence-1)
|
||
|
||
[2](#format.context-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7708)
|
||
|
||
If a program declares an explicit or partial specialization ofbasic_format_context,
|
||
the program is ill-formed, no diagnostic required[.](#format.context-2.sentence-1)
|
||
|
||
[3](#format.context-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7713)
|
||
|
||
Out shall model [output_iterator](iterator.concept.output#concept:output_iterator "24.3.4.10 Concept output_iterator [iterator.concept.output]")<const charT&>[.](#format.context-3.sentence-1)
|
||
|
||
[4](#format.context-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7716)
|
||
|
||
format_context is an alias for
|
||
a specialization of basic_format_context with an output iterator
|
||
that appends to string,
|
||
such as back_insert_iterator<string>[.](#format.context-4.sentence-1)
|
||
|
||
Similarly, wformat_context is an alias for
|
||
a specialization of basic_format_context with an output iterator
|
||
that appends to wstring[.](#format.context-4.sentence-2)
|
||
|
||
[5](#format.context-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7729)
|
||
|
||
*Recommended practice*: For a given type charT,
|
||
implementations should provide
|
||
a single instantiation of basic_format_context for appending tobasic_string<charT>,vector<charT>,
|
||
or any other container with contiguous storage
|
||
by wrapping those in temporary objects with a uniform interface
|
||
(such as a span<charT>) and polymorphic reallocation[.](#format.context-5.sentence-1)
|
||
|
||
[ð](#lib:arg,basic_format_context)
|
||
|
||
`basic_format_arg<basic_format_context> arg(size_t id) const noexcept;
|
||
`
|
||
|
||
[6](#format.context-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7747)
|
||
|
||
*Returns*: args_.get(id)[.](#format.context-6.sentence-1)
|
||
|
||
[ð](#lib:locale,basic_format_context)
|
||
|
||
`std::locale locale();
|
||
`
|
||
|
||
[7](#format.context-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7758)
|
||
|
||
*Returns*: The locale passed to the formatting function
|
||
if the latter takes one,
|
||
and std::locale() otherwise[.](#format.context-7.sentence-1)
|
||
|
||
[ð](#lib:out,basic_format_context)
|
||
|
||
`iterator out();
|
||
`
|
||
|
||
[8](#format.context-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7771)
|
||
|
||
*Effects*: Equivalent to: return std::move(out_);
|
||
|
||
[ð](#lib:advance_to,basic_format_context)
|
||
|
||
`void advance_to(iterator it);
|
||
`
|
||
|
||
[9](#format.context-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7782)
|
||
|
||
*Effects*: Equivalent to: out_ = std::move(it);
|
||
|
||
[*Example [1](#format.context-example-1)*: struct S { int value; };
|
||
|
||
template<> struct std::formatter<S> { size_t width_arg_id = 0; // Parses a width argument id in the format { *digit* }.constexpr auto parse(format_parse_context& ctx) {auto iter = ctx.begin(); auto is_digit = [](auto c) { return c >= '0' && c <= '9'; }; auto get_char = [&]() { return iter != ctx.end() ? *iter : 0; }; if (get_char() != '{')return iter; ++iter; char c = get_char(); if (!is_digit(c) || (++iter, get_char()) != '}')throw format_error("invalid format");
|
||
width_arg_id = c - '0';
|
||
ctx.check_arg_id(width_arg_id); return ++iter; }// Formats an S with width given by the argument width_arg_id.auto format(S s, format_context& ctx) const {int width = ctx.arg(width_arg_id).visit([](auto value) -> int {if constexpr (!is_integral_v<decltype(value)>)throw format_error("width is not integral"); else if (value < 0 || value > numeric_limits<int>::max())throw format_error("invalid width"); elsereturn value; }); return format_to(ctx.out(), "{0:x>{1}}", s.value, width); }};
|
||
|
||
std::string s = std::format("{0:{1}}", S{42}, 10); // value of s is "xxxxxxxx42" â *end example*]
|