pair
/tuple
formattersSection: 28.5.7.4 [format.range.fmtmap] Status: New Submitter: Victor Zverovich Opened: 2024-05-18 Last modified: 2024-05-19
Priority: Not Prioritized
View all issues with New status.
Discussion:
Consider the following example:
#include <format> #include <map> #include <print> struct x {}; template<typename K> struct std::formatter<std::pair<K, x>> : std::formatter<std::string_view> { auto format(const std::pair<K, x>& p, auto& ctx) const { return std::format_to(ctx.out(), "x/x"); } }; int main() { std::print("{}", std::map<x, x>()); }
It doesn't compile because the formatter for maps requires the element formatter to have
set_brackets
and set_separator
(28.5.7.4 [format.range.fmtmap]):
underlying_.underlying().set_brackets({}, {}); underlying_.underlying().set_separator(STATICALLY-WIDEN<charT>(": "));
The specialization std::formatter<std::pair<K, x>>
itself is allowed according to
16.4.5.2.1 [namespace.std]:
Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace
std
provided that
the added declaration depends on at least one program-defined type, and
the specialization meets the standard library requirements for the original template.
but it's unclear what exactly the part "the specialization meets the standard library requirements for
the original template" means for this formatter. Does it mean that the specialization must provide
set_brackets
and set_separator
and does the output have to be consistent with the main
template? The latter would render the specialization useless. On the other hand if users are allowed to
customize pair and tuple formatting the current specification of the map formatter is broken.
pair
s
and tuple
s, and make map be responsible for its own structural formatting rather than delegating
part of it to other formatters in an arbitrary way. This resolution has been applied to {fmt}'s implementation
of range formatting to address #3685.
Proposed resolution:
This wording is relative to N4981.
Modify 28.5.8.3 [format.args] as indicated:
namespace std { template<ranges::input_range R, class charT> struct range-default-formatter<range_format::map, R, charT> { private: using maybe-const-map = fmt-maybe-const<R, charT>; // exposition only using element-type = // exposition only remove_cvref_t<ranges::range_reference_t<maybe-const-map>>;range_formatter<element-type, charT> underlying_; // exposition onlyusing key-type = tuple_element_t<0, element-type>; // exposition only using value-type = tuple_element_t<1, element-type>; // exposition only formatter<key-type, charT> key-formatter_; // exposition only formatter<value-type, charT> value-formatter_; // exposition only public: constexpr range-default-formatter(); template<class ParseContext> constexpr typename ParseContext::iterator parse(ParseContext& ctx); template<class FormatContext> typename FormatContext::iterator format(maybe-const-map& r, FormatContext& ctx) const; }; }constexpr range-default-formatter();-1- Mandates: Either:
(1.1) —
element-type
is a specialization ofpair
, or(1.2) —
element-type
is a specialization oftuple
andtuple_size_v<element-type> == 2
.
-2- Effects: Equivalent to:underlying_.set_brackets(STATICALLY-WIDEN<charT>("{"), STATICALLY-WIDEN<charT>("}")); underlying_.underlying().set_brackets({}, {}); underlying_.underlying().set_separator(STATICALLY-WIDEN<charT>(": "));template<class ParseContext> constexpr typename ParseContext::iterator parse(ParseContext& ctx);-3- Effects:
IfEquivalent to:Parses the format specifiers as a range-format-spec and stores the parsed specifiers inreturn underlying_.parse(ctx);
*this
.key-formatter_.set_debug_format()
is a valid expression, and there is no range-underlying-spec, then callskey-formatter_.set_debug_format()
. Ifvalue-formatter_.set_debug_format()
is a valid expression, and there is no range-underlying-spec, then callsvalue-formatter_.set_debug_format()
. -?- Returns: An iterator past the end of the range-format-spec.template<class FormatContext> typename FormatContext::iterator format(maybe-const-map& r, FormatContext& ctx) const;-4- Effects:
Equivalent to:Writes the following intoreturn underlying_.format(r, ctx);
ctx.out()
, adjusted according to the range-format-spec:
—
STATICALLY-WIDEN<charT>("{")
unless then
option is specified,— for each element
e
of the ranger
:
— the result of writing
get<0>(e)
viakey-formatter_
,—
STATICALLY-WIDEN<charT>(": ")
,— the result of writing
get<1>(e)
viavalue-formatter_
,—
STATICALLY-WIDEN<charT>(", ")
, unlesse
is the last element ofr
, and—
STATICALLY-WIDEN<charT>("}")
unless then
option is specified.-?- Returns: An iterator past the end of the output range.