[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, - [(3.6)](#formatter.requirements-3.6) FC is basic_format_context, - [(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)
**Expression** | **Return type** | **Requirement** | | --- | --- | --- | | [🔗](#tab:formatter.basic-row-2)
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)
Throws format_error unless the whole range is parsed or the unmatched character is }[.](#tab:formatter.basic-row-2-column-3-sentence-2)
[*Note [1](#tab:formatter.basic-row-2-column-3-note-1)*:
This allows formatters to emit meaningful error messages[.](#tab:formatter.basic-row-2-column-3-sentence-3) — *end note*]
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)
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)
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)
**Expression** | **Return type** | **Requirement** | | --- | --- | --- | | [🔗](#tab:formatter-row-2)
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)
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)
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 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* be an unspecified type that models[output_iterator](iterator.concept.output#concept:output_iterator "24.3.4.10 Concept output_­iterator [iterator.concept.output]") ([[iterator.concept.output]](iterator.concept.output "24.3.4.10 Concept output_­iterator"))[.](#format.formattable-1.sentence-1) template>>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]") &&requires(Formatter& f, const Formatter& cf, T&& t, Context fc, basic_format_parse_context pc){{ f.parse(pc) } -> [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]"); { cf.format(t, fc) } -> [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]"); }; templateconcept [formattable](#concept:formattable "28.5.6.3 Concept formattable [format.formattable]") =[*formattable-with*](#concept:formattable-with "28.5.6.3 Concept formattable [format.formattable]"), basic_format_context<*fmt-iter-for*, 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, charT> meets the *BasicFormatter* requirements ([[formatter.requirements]](#formatter.requirements "28.5.6.1 Formatter requirements")) and, if remove_reference_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;template<> struct formatter;template<> struct formatter; - [(2.2)](#spec-2.2) For each charT, the debug-enabled string type specializationstemplate<> struct formatter;template<> struct formatter;template struct formatter;templatestruct formatter, charT>;templatestruct formatter, 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; - [(2.4)](#spec-2.4) For each charT, the pointer type specializationstemplate<> struct formatter;template<> struct formatter;template<> struct formatter; 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 = true; [*Note [1](#spec-note-1)*: Specializations such as formatter 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 provides the following disabled specializations: - [(4.1)](#spec-4.1) The string type specializationstemplate<> struct formatter;template<> struct formatter;template struct formatter;templatestruct formatter, wchar_t>;templatestruct formatter, 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 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, 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, - [(7.2)](#spec-7.2) is_copy_constructible_v, - [(7.3)](#spec-7.3) is_move_constructible_v, - [(7.4)](#spec-7.4) is_copy_assignable_v, and - [(7.5)](#spec-7.5) is_move_assignable_v[.](#spec-7.sentence-1) [8](#spec-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7258) An enabled specialization formatter meets the*BasicFormatter* requirements ([[formatter.requirements]](#formatter.requirements "28.5.6.1 Formatter requirements"))[.](#spec-8.sentence-1) [*Example [1](#spec-example-1)*: #include #include enum color { red, green, blue };const char* color_names[] = { "red", "green", "blue" }; template<> struct std::formatter : std::formatter {auto format(color c, format_context& ctx) const {return formatter::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)
**Character** | **Escape sequence** | | --- | --- | | [🔗](#tab:format.escape.sequences-row-2)
U+0009 character tabulation | \t | | [🔗](#tab:format.escape.sequences-row-3)
U+000a line feed | \n | | [🔗](#tab:format.escape.sequences-row-4)
U+000d carriage return | \r | | [🔗](#tab:format.escape.sequences-row-5)
U+0022 quotation mark | \" | | [🔗](#tab:format.escape.sequences-row-6)
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: ["ẹ́"] — *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 {templateclass basic_format_parse_context {public:using char_type = charT; using const_iterator = typename basic_string_view::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 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); templateconstexpr 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 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 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, 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) 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(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>(id); #### [28.5.6.7](#format.context) Class template basic_format_context [[format.context]](format.context) [🔗](#lib:basic_format_context) namespace std {templateclass basic_format_context { basic_format_args 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 using formatter_type = formatter; basic_format_arg 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]")[.](#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[.](#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,vector, or any other container with contiguous storage by wrapping those in temporary objects with a uniform interface (such as a span) and polymorphic reallocation[.](#format.context-5.sentence-1) [🔗](#lib:arg,basic_format_context) `basic_format_arg 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 { 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)throw format_error("width is not integral"); else if (value < 0 || value > numeric_limits::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*]