[format.formatter] (original) (raw)
28.5.6.1 Formatter requirements [formatter.requirements]
A type F meets the BasicFormatter requirements if it meets the
- Cpp17DefaultConstructible (Table 30),
- Cpp17CopyConstructible (Table 32),
- Cpp17CopyAssignable (Table 34),
- Cpp17Swappable ([swappable.requirements]), and
- Cpp17Destructible (Table 35)
requirements, and the expressions shown in Table 110 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 111 are valid and have the indicated semantics.
Given character type charT, output iterator typeOut, and formatting argument type T, in Table 110 and Table 111:
- 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 eitherpc.begin() == pc.end() or*pc.begin() == '}'.
Table 110 — BasicFormatter requirements [tab:formatter.basic]
Parses format-spec ([format.string]) for type Tin 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. |
---|
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 onu,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 111 — Formatter requirements [tab:formatter]
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 ont,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). |
---|
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]
template<class T, class Context,class Formatter = typename Context::template formatter_type<remove_const_t<T>>> concept formattable-with = 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 charTmodel formattableif 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 formatteradditionally 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 formatterprovides the following enabled specializations:
- The debug-enabled specializationstemplate<> 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 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>;
- For each charT, for each cv-unqualified arithmetic type ArithmeticTother thanchar,wchar_t,char8_t,char16_t, orchar32_t, a specializationtemplate<> struct formatter<ArithmeticT, charT>;
- 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].
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 provides the following disabled specializations:
- 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>;
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 offormatter<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); std::string s1 = std::format("{}", L"foo"); std::string s2 = std::format("{}", red); std::string s3 = std::format("{}", err{}); — _end example_]
28.5.6.5 Formatting escaped characters and strings [format.string.escaped]
A character or string can be formatted as escapedto 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 112, 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 groupsSeparator (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=Yesas 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.
- If X encodes a single character C, then:
- Finally, U+0022 quotation mark (") is appended to E.
Table 112 — 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"); string s1 = format("[{:?}]", "h\tllo"); string s2 = format("[{:?}]", "Спасибо, Виктор ♥!"); string s3 = format("[{:?}, {:?}]", '\'', '"'); string s4 = format("[{:?}]", string("\0 \n \t \x02 \x1b", 9)); string s5 = format("[{:?}]", "\xc3\x28"); string s6 = format("[{:?}]", "🤷🏻♂️"); string s7 = format("[{:?}]", "\u0301"); string s8 = format("[{:?}]", "\\\u0301"); string s9 = format("[{:?}]", "e\u0301\u0323"); — _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_; iterator end_; enum indexing { unknown, manual, automatic }; indexing indexing_; size_t next_arg_id_; size_t num_args_; 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 ofbasic_format_parse_context, the program is ill-formed, no diagnostic required.
constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt) noexcept;
Effects: Initializesbegin_ with fmt.begin(),end_ with fmt.end(),indexing_ with unknown,next_arg_id_ with 0, andnum_args_ with 0.
[Note 1:
Any call tonext_arg_id, check_arg_id, or check_dynamic_specon an instance of basic_format_parse_contextinitialized using this constructor is not a core constant expression.
— _end note_]
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
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 trueare 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 ifindexing_ == 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 ifid < 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 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*.
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_; Out out_; 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 ofbasic_format_context, the program is ill-formed, no diagnostic required.
format_context is an alias for a specialization of basic_format_contextwith 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_contextwith an output iterator that appends to wstring.
Recommended practice: For a given type charT, implementations should provide a single instantiation of basic_format_contextfor 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.
basic_format_arg<basic_format_context> arg(size_t id) const noexcept;
Returns: The locale passed to the formatting function if the latter takes one, and std::locale() otherwise.
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;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;} 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); — _end example_]