28 Text processing library [text]

28.5 Formatting [format]

28.5.6 Formatter [format.formatter]

28.5.6.1 Formatter requirements [formatter.requirements]

A type F meets the BasicFormatter requirements if it meets the requirements, and the expressions shown in Table 107 are valid and have the indicated semantics.
A type F meets the Formatter requirements if it meets the BasicFormatter requirements and the expressions shown in Table 108 are valid and have the indicated semantics.
Given character type charT, output iterator type Out, and formatting argument type T, in Table 107 and Table 108:
  • f is a value of type (possibly const) F,
  • g is an lvalue of type F,
  • u is an lvalue of type T,
  • t is a value of a type convertible to (possibly const) T,
  • PC is basic_format_parse_context<charT>,
  • FC is basic_format_context<Out, charT>,
  • pc is an lvalue of type PC, and
  • fc is an lvalue of type FC.
pc.begin() points to the beginning of the format-spec ([format.string]) of the replacement field being formatted in the format string.
If format-spec is not present or empty then either pc.begin() == pc.end() or *pc.begin() == '}'.
Table 107: BasicFormatter requirements [tab:formatter.basic]
Expression
Return type
Requirement
g.parse(pc)
PC​::​iterator
Parses format-spec ([format.string]) for type T in the range [pc.begin(), pc.end()) until the first unmatched character.
Throws format_error unless the whole range is parsed or the unmatched character is }.
[Note 1: 
This allows formatters to emit meaningful error messages.
β€” end note]
Stores the parsed format specifiers in *this and returns an iterator past the end of the parsed range.
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.
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).
Table 108: Formatter requirements [tab:formatter]
Expression
Return type
Requirement
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.
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).
f.format(u, fc)
FC​::​iterator
As above, but does not modify u.

28.5.6.2 Formatter locking [format.formatter.locking]

template<class T> constexpr bool enable_nonlocking_formatter_optimization = false;
Remarks: Pursuant to [namespace.std], users may specialize enable_nonlocking_formatter_optimization for cv-unqualified program-defined types.
Such specializations shall be usable in constant expressions ([expr.const]) and have type const bool.

28.5.6.3 Concept formattable [format.formattable]

Let fmt-iter-for<charT> be an unspecified type that models output_iterator<const charT&> ([iterator.concept.output]).
template<class T, class Context, class Formatter = typename Context::template formatter_type<remove_const_t<T>>> concept formattable-with = // exposition only semiregular<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<typename decltype(pc)::iterator>; { cf.format(t, fc) } -> same_as<typename Context::iterator>; }; template<class T, class charT> concept formattable = formattable-with<remove_reference_t<T>, basic_format_context<fmt-iter-for<charT>, charT>>;
A type T and a character type charT model formattable if formatter<remove_cvref_t<T>, charT> meets the BasicFormatter requirements ([formatter.requirements]) and, if remove_reference_t<T> is const-qualified, the Formatter requirements.

28.5.6.4 Formatter specializations [format.formatter.spec]

The functions defined in [format.functions] use specializations of the class template formatter to format individual arguments.
Let charT be either char or wchar_t.
Each specialization of formatter is either enabled or disabled, as described below.
A debug-enabled 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 ?.
Each header that declares the template formatter provides the following enabled specializations:
  • The debug-enabled specializations template<> struct formatter<char, char>; template<> struct formatter<char, wchar_t>; template<> struct formatter<wchar_t, wchar_t>;
  • For each charT, the debug-enabled string type specializations template<> 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>;
  • For each charT, for each cv-unqualified arithmetic type ArithmeticT other than char, wchar_t, char8_t, char16_t, or char32_t, a specialization template<> struct formatter<ArithmeticT, charT>;
  • For each charT, the pointer type specializations template<> 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].
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: 
Specializations such as formatter<wchar_t, char> that would require implicit multibyte / wide string or character conversion are disabled.
β€” end note]
The header <format> provides the following disabled specializations:
  • The string type specializations template<> 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>;
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.
If the library provides an explicit or partial specialization of formatter<T, charT>, that specialization is enabled and meets the Formatter requirements except as noted otherwise.
If F is a disabled specialization of formatter, these values are false:
  • is_default_constructible_v<F>,
  • is_copy_constructible_v<F>,
  • is_move_constructible_v<F>,
  • is_copy_assignable_v<F>, and
  • is_move_assignable_v<F>.
An enabled specialization formatter<T, charT> meets the BasicFormatter requirements ([formatter.requirements]).
[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 Formatting escaped characters and strings [format.string.escaped]

A character or string can be formatted as escaped to make it more suitable for debugging or for logging.
The escaped string E representation of a string S is constructed by encoding a sequence of characters as follows.
The associated character encoding CE for charT (Table 12) is used to both interpret S and construct E.
  • U+0022 quotation mark (") is appended to E.
  • 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:
    • If X encodes a single character C, then:
      • If C is one of the characters in Table 109, then the two characters shown as the corresponding escape sequence are appended to E.
      • Otherwise, if C is not U+0020 space and
        • 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 groups Separator (Z) or Other (C), as described by UAX #44 of the Unicode Standard, or
        • 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
        • 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.
      • Otherwise, C is appended to E.
    • Otherwise, if X is a shift sequence, the effect on E and further decoding of S is unspecified.
      Recommended practice: A shift sequence should be represented in E such that the original code unit sequence of S can be reconstructed.
    • 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.
  • Finally, U+0022 quotation mark (") is appended to E.
Table 109: Mapping of characters to escape sequences [tab:format.escape.sequences]
Character
Escape sequence
U+0009 character tabulation
\t
U+000a line feed
\n
U+000d carriage return
\r
U+0022 quotation mark
\"
U+005c reverse solidus
\\
The escaped string representation of a character C is equivalent to the escaped string representation of a string of C, except that:
  • the result starts and ends with U+0027 apostrophe (') instead of U+0022 quotation mark ("), and
  • if C is U+0027 apostrophe, the two characters \' are appended to E, and
  • if C is U+0022 quotation mark, then C is appended unchanged.
[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 Class template basic_format_parse_context [format.parse.ctx]

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; }; }
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.
If a program declares an explicit or partial specialization of basic_format_parse_context, the program is ill-formed, no diagnostic required.
constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt) noexcept;
Effects: Initializes begin_ with fmt.begin(), end_ with fmt.end(), indexing_ with unknown, next_arg_id_ with 0, and num_args_ with 0.
[Note 1: 
Any call to next_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.
β€” end note]
constexpr const_iterator begin() const noexcept;
Returns: begin_.
constexpr const_iterator end() const noexcept;
Returns: end_.
constexpr void advance_to(const_iterator it);
Preconditions: end() is reachable from it.
Effects: Equivalent to: begin_ = it;
constexpr size_t next_arg_id();
Effects: If indexing_ != manual is true, equivalent to: if (indexing_ == unknown) indexing_ = automatic; return next_arg_id_++;
Throws: format_error if indexing_ == manual is true.
[Note 2: 
This indicates mixing of automatic and manual argument indexing.
β€” end note]
Remarks: Let cur-arg-id be the value of next_arg_id_ prior to this call.
Call expressions where cur-arg-id >= num_args_ is true are not core constant expressions ([expr.const]).
constexpr void check_arg_id(size_t id);
Effects: If indexing_ != automatic is true, equivalent to: if (indexing_ == unknown) indexing_ = manual;
Throws: format_error if indexing_ == automatic is true.
[Note 3: 
This indicates mixing of automatic and manual argument indexing.
β€” end note]
Remarks: A call to this function is a core constant expression ([expr.const]) only if id < num_args_ is true.
template<class... Ts> constexpr void check_dynamic_spec(size_t id) noexcept;
Mandates: sizeof...(Ts)  β‰₯ 1.
The types in Ts... are unique.
Each type in Ts... is one of bool, char_type, int, unsigned int, long long int, unsigned long long int, float, double, long double, const char_type*, basic_string_view<char_type>, or const void*.
Remarks: A call to this function is a core constant expression only if
  • id < num_args_ is true and
  • the type of the corresponding format argument (after conversion to basic_format_arg<Context>) is one of the types in Ts....
constexpr void check_dynamic_spec_integral(size_t id) noexcept;
Effects: Equivalent to: check_dynamic_spec<int, unsigned int, long long int, unsigned long long int>(id);
constexpr void check_dynamic_spec_string(size_t id) noexcept;
Effects: Equivalent to: check_dynamic_spec<const char_type*, basic_string_view<char_type>>(id);

28.5.6.7 Class template basic_format_context [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); }; }
An instance of basic_format_context holds formatting state consisting of the formatting arguments and the output iterator.
If a program declares an explicit or partial specialization of basic_format_context, the program is ill-formed, no diagnostic required.
Out shall model output_iterator<const charT&>.
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>.
Similarly, wformat_context is an alias for a specialization of basic_format_context with an output iterator that appends to wstring.
Recommended practice: For a given type charT, implementations should provide a single instantiation of basic_format_context for appending to basic_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.
basic_format_arg<basic_format_context> arg(size_t id) const noexcept;
Returns: args_.get(id).
std::locale locale();
Returns: The locale passed to the formatting function if the latter takes one, and std​::​locale() otherwise.
iterator out();
Effects: Equivalent to: return std​::​move(out_);
void advance_to(iterator it);
Effects: Equivalent to: out_ = std​::​move(it);
[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"); else return 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]