20 General utilities library [utilities]

20.20 Formatting [format]

20.20.5 Formatter [format.formatter]

20.20.5.1 Formatter requirements [formatter.requirements]

A type F meets the Formatter requirements if:
Given character type charT, output iterator type Out, and formatting argument type T, in Table 67:
  • f is a value 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 empty then either pc.begin() == pc.end() or *pc.begin() == '}'.
Table 67: Formatter requirements   [tab:formatter]
Expression
Return type
Requirement
f.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
:
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(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(), 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.

20.20.5.2 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.
[Note
:
Enabled specializations meet the Formatter requirements, and disabled specializations do not.
— end note
]
Each header that declares the template formatter provides the following enabled specializations:
  • The specializations
    template<> struct formatter<char, char>;
    template<> struct formatter<char, wchar_t>;
    template<> struct formatter<wchar_t, wchar_t>;
    
  • For each charT, the string type specializations
    template<> struct formatter<charT*, charT>;
    template<> struct formatter<const charT*, charT>;
    template<size_t N> struct formatter<const 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].
[Note
:
Specializations such as formatter<wchar_­t, char> and formatter<const char*, wchar_­t> that would require implicit multibyte / wide string or character conversion are disabled.
— end note
]
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 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 Formatter requirements ([formatter.requirements]).
[Example
:
#include <format>

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) {
    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
]

20.20.5.3 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,
                                                  size_t num_args = 0) 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);
  };
}
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.
constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt, size_t num_args = 0) noexcept;
Effects: Initializes begin_­ with fmt.begin(), end_­ with fmt.end(), indexing_­ with unknown, next_­arg_­id_­ with 0, and num_­args_­ with num_­args.
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, equivalent to:
if (indexing_ == unknown)
  indexing_ = automatic;
return next_arg_id_++;
Throws: format_­error if indexing_­ == manual which indicates mixing of automatic and manual argument indexing.
constexpr void check_arg_id(size_t id);
Effects: If indexing_­ != automatic, equivalent to:
if (indexing_ == unknown)
  indexing_ = manual;
Throws: format_­error if indexing_­ == automatic which indicates mixing of automatic and manual argument indexing.
Remarks: Call expressions where id >= num_­args_­ are not core constant expressions ([expr.const]).

20.20.5.4 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

  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;
    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.
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.
[Note
:
For a given type charT, implementations are encouraged to 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.
— end note
]
basic_format_arg<basic_format_context> arg(size_t id) const;
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();
Returns: out_­.
void advance_to(iterator it);
Effects: Equivalent to: out_­ = it;
[Example
:
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 get_char = [&]() { return iter != ctx.end() ? *iter : 0; };
    if (get_char() != '{')
      return iter;
    ++iter;
    char c = get_char();
    if (!isdigit(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) {
    int width = visit_format_arg([](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;
      }, ctx.arg(width_arg_id));
    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
]