Created on 2024-05-18.00:00:00 last changed 6 months ago
Proposed resolution:
This wording is relative to N4981.
Modify [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 of pair, or
(1.2) — element-type is a specialization of tuple and tuple_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:
If key-formatter_.set_debug_format() is a valid expression, and there is no range-underlying-spec, then calls key-formatter_.set_debug_format(). If value-formatter_.set_debug_format() is a valid expression, and there is no range-underlying-spec, then calls value-formatter_.set_debug_format(). -?- Returns: An iterator past the end of the range-format-spec.Equivalent to: return underlying_.parse(ctx);Parses the format specifiers as a range-format-spec and stores the parsed specifiers in *this.template<class FormatContext> typename FormatContext::iterator format(maybe-const-map& r, FormatContext& ctx) const;-4- Effects:
Equivalent to: return underlying_.format(r, ctx);Writes the following into ctx.out(), adjusted according to the range-format-spec:
— STATICALLY-WIDEN<charT>("{") unless the n option is specified,
— for each element e of the range r:
— the result of writing get<0>(e) via key-formatter_,
— STATICALLY-WIDEN<charT>(": "),
— the result of writing get<1>(e) via value-formatter_,
— STATICALLY-WIDEN<charT>(", "), unless e is the last element of r, and
— STATICALLY-WIDEN<charT>("}") unless the n option is specified.
-?- Returns: An iterator past the end of the output range.
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 ([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 [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.
The correct resolution appears to be to not restrict user-defined formatter specializations of pairs and tuples, 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.History | |||
---|---|---|---|
Date | User | Action | Args |
2024-05-19 12:26:01 | admin | set | messages: + msg14156 |
2024-05-18 00:00:00 | admin | create |