[format.formatter] (original) (raw)

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 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:

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 110BasicFormatter 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 111Formatter 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 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:

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:

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.

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:

[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

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_]