Difference between revisions of "cpp/utility/format/range formatter"
(added c++23 std::range_formatter) |
m (`assert` is more expressive.) |
||
(11 intermediate revisions by 4 users not shown) | |||
Line 6: | Line 6: | ||
{{dcl|since=c++23|1= | {{dcl|since=c++23|1= | ||
template< class T, class CharT = char > | template< class T, class CharT = char > | ||
− | + | requires std::same_as<std::remove_cvref_t<T>, T> && std::formattable<T, CharT> | |
class range_formatter; | class range_formatter; | ||
}} | }} | ||
Line 21: | Line 21: | ||
{{sdsc end}} | {{sdsc end}} | ||
− | The {{spar|range-fill-and-align}} is interpreted the same way as a {{spar|fill-and-align}} except that the {{spar|fill}} in {{spar|range-fill-and-align}} is any character other than {{ttb| | + | The {{spar|range-fill-and-align}} is interpreted the same way as a {{spar|fill-and-align}} except that the {{spar|fill}} in {{spar|range-fill-and-align}} is any character other than {{ttb|{}}, {{ttb|}<!---->}}, or {{ttb|:}}. |
− | The {{spar|width}} is described in | + | The {{spar|width}} is described in {{rlp|spec#Width and precision|standard format width specification}}. |
The {{ttb|n}} option causes the range to be formatted without the opening and closing brackets. | The {{ttb|n}} option causes the range to be formatted without the opening and closing brackets. | ||
+ | {{source|1= | ||
+ | assert(std::format("{}", views::iota(1, 5)) == "[1, 2, 3, 4]"); | ||
+ | assert(std::format("{:n}", views::iota(1, 5)) == "1, 2, 3, 4"); | ||
+ | }} | ||
The {{spar|format-spec}} in a {{spar|range-underlying-spec}} (its syntax is equivalent to {{tt|:}} {{spar|format-spec}}), if any, is interpreted by the range element formatter {{tt|std::formatter<T, CharT>}}. | The {{spar|format-spec}} in a {{spar|range-underlying-spec}} (its syntax is equivalent to {{tt|:}} {{spar|format-spec}}), if any, is interpreted by the range element formatter {{tt|std::formatter<T, CharT>}}. | ||
+ | {{source|1= | ||
+ | std::array ints{12, 10, 15, 14}; | ||
− | The {{spar|range-type}} changes the way a range is formatted, with certain options only valid with certain argument types. | + | assert(std::format("{}", ints) == "[12, 10, 15, 14]"); |
+ | assert(std::format("{::X}", ints) == "[C, A, F, E]"); | ||
+ | assert(std::format("{:n:_^4}", ints) == "_12_, _10_, _15_, _14_"); | ||
+ | }} | ||
+ | |||
+ | The {{spar|range-type}} changes the way a range is formatted, with certain options only valid with certain argument types. | ||
The available range presentation types are: | The available range presentation types are: | ||
− | * {{ttb|m}}: Indicates that the opening bracket should be {{c|"{"}}, the closing bracket should be {{c|"}"}}, the separator should be {{c|", "}}, and each range element should be formatted as if {{ttb|m}} were specified for its {{spar|tuple-type}} ( | + | * {{ttb|m}}: Indicates that the opening bracket should be {{c|"{"}}, the closing bracket should be {{c|"}"}}, the separator should be {{c|", "}}, and each range element should be formatted as if {{ttb|m}} were specified for its {{spar|tuple-type}} (in [[cpp/utility/format/tuple_formatter#Format specification|{{spar|tuple-format-spec}}]]). |
:* If {{ttb|m}} is chosen as the {{spar|range-type}}, the program is ill-formed unless {{tt|T}} is either a specialization of: | :* If {{ttb|m}} is chosen as the {{spar|range-type}}, the program is ill-formed unless {{tt|T}} is either a specialization of: | ||
::* {{lc|std::pair}}, or | ::* {{lc|std::pair}}, or | ||
− | ::* {{lc|std::tuple}} such that {{c|std::tuple_size_v<T> | + | ::* {{lc|std::tuple}} such that {{c|1=std::tuple_size_v<T> == 2}} is {{c|true}}. |
+ | {{source|1= | ||
+ | std::array char_pairs | ||
+ | { | ||
+ | std::pair{'A', 5}, std::pair{'B', 10}, std::pair{'C', 12} | ||
+ | }; | ||
− | + | assert(std::format("{}", char_pairs) == "[('A', 5), ('B', 10), ('C', 12)]"); | |
− | + | assert(std::format("{:m}", char_pairs) == "{'A': 5, 'B': 10, 'C': 12}"); | |
+ | }} | ||
+ | * {{ttb|s}}: Indicates that the range should be formatted as a string. | ||
+ | * {{ttb|?s}}: Indicates that the range should be formatted as an {{rlp|spec#Formatting escaped characters and strings|escaped string}}. | ||
:* If {{ttb|s}} or {{ttb|?s}} is chosen as the {{spar|range-type}}, both {{ttb|n}} option and {{spar|range-underlying-spec}} should not be included in the format specifier, and | :* If {{ttb|s}} or {{ttb|?s}} is chosen as the {{spar|range-type}}, both {{ttb|n}} option and {{spar|range-underlying-spec}} should not be included in the format specifier, and | ||
:* the program is ill-formed unless {{tt|T}} is {{tt|CharT}}. | :* the program is ill-formed unless {{tt|T}} is {{tt|CharT}}. | ||
+ | {{source|1= | ||
+ | std::array star{'S', 'T', 'A', 'R'}; | ||
+ | |||
+ | assert(std::format("{}", star) == "['S', 'T', 'A', 'R']"); | ||
+ | assert(std::format("{:s}", star) == "STAR"); | ||
+ | assert(std::format("{:?s}", star) == "\"STAR\""); | ||
+ | }} | ||
===Member objects=== | ===Member objects=== | ||
{{dsc begin}} | {{dsc begin}} | ||
{{dsc hitem|Member name|Definition}} | {{dsc hitem|Member name|Definition}} | ||
− | {{dsc| | + | {{dsc expos mem obj|private=yes|underlying_|the underlying formatter of type {{c/core|std::formatter<T, CharT>}}}} |
− | {{dsc| | + | {{dsc expos mem obj|private=yes|separator_|a string representing the separator of the range formatted result. The default separator is {{c|", "}}.}} |
− | {{dsc| | + | {{dsc expos mem obj|private=yes|opening-bracket_|a string representing the opening bracket of the range formatted result. The default opening bracket is {{c|"["}}.}} |
− | {{dsc| | + | {{dsc expos mem obj|private=yes|closing-bracket_|a string representing the closing bracket of the range formatted result. The default closing bracket is {{c|"]"}}.}} |
{{dsc end}} | {{dsc end}} | ||
Line 62: | Line 88: | ||
{{dsc end}} | {{dsc end}} | ||
− | {{member | 1={{small|std::range_formatter::}}set_separator |2= | + | {{member|1={{small|std::range_formatter::}}set_separator|2= |
{{dcl begin}} | {{dcl begin}} | ||
− | {{dcl | 1= | + | {{dcl|1= |
constexpr void set_separator( std::basic_string_view<CharT> sep ) noexcept; | constexpr void set_separator( std::basic_string_view<CharT> sep ) noexcept; | ||
}} | }} | ||
{{dcl end}} | {{dcl end}} | ||
− | Assigns {{ | + | Assigns {{c|sep}} to {{tti|separator_}}. |
}} | }} | ||
− | {{member | 1={{small|std::range_formatter::}}set_brackets |2= | + | {{member|1={{small|std::range_formatter::}}set_brackets|2= |
{{dcl begin}} | {{dcl begin}} | ||
− | {{dcl | 1= | + | {{dcl|1= |
constexpr void set_brackets( std::basic_string_view<CharT> opening, | constexpr void set_brackets( std::basic_string_view<CharT> opening, | ||
std::basic_string_view<CharT> closing ) noexcept; | std::basic_string_view<CharT> closing ) noexcept; | ||
Line 80: | Line 106: | ||
{{dcl end}} | {{dcl end}} | ||
− | Assigns {{ | + | Assigns {{c|opening}} and {{c|closing}} to {{tti|opening-bracket_}} and {{tti|closing-bracket_}}, respectively. |
}} | }} | ||
− | {{member | 1={{small|std::range_formatter::}} | + | {{member|1={{small|std::range_formatter::}}underlying|2= |
{{dcl begin}} | {{dcl begin}} | ||
− | {{dcl | 1= | + | {{dcl|num=1|1= |
+ | constexpr std::formatter<T, CharT>& underlying(); | ||
+ | }} | ||
+ | {{dcl|num=2|1= | ||
+ | constexpr const std::formatter<T, CharT>& underlying() const; | ||
+ | }} | ||
+ | {{dcl end}} | ||
+ | |||
+ | Returns {{box|{{tti|underlying_}}}} (the underlying formatter). | ||
+ | }} | ||
+ | |||
+ | {{member|1={{small|std::range_formatter::}}parse|2= | ||
+ | {{dcl begin}} | ||
+ | {{dcl|1= | ||
template< class ParseContext > | template< class ParseContext > | ||
− | constexpr auto parse( ParseContext& ctx ) -> | + | constexpr auto parse( ParseContext& ctx ) -> ParseContext::iterator; |
}} | }} | ||
{{dcl end}} | {{dcl end}} | ||
− | Parses the format | + | Parses the format specifiers as a {{spar|range-format-spec}} and stores the parsed specifiers in the current object. |
− | + | Calls {{box|{{tti|underlying_}}{{c/core|.parse(ctx)}}}} to parse {{spar|format-spec}} in {{spar|range-format-spec}} or, if the latter is not present, an empty {{spar|format-spec}}. | |
− | It calls {{ | + | If {{spar|range-type}} or the {{ttb|n}} option is present, the values of {{tti|opening-bracket_}}, {{tti|closing-bracket_}}, and {{tti|separator_}} are modified as required. |
+ | |||
+ | It calls {{box|{{tti|underlying_}}{{c/core|.set_debug_format()}}}} if: | ||
* the {{spar|range-type}} is neither {{ttb|s}} nor {{ttb|?s}}, | * the {{spar|range-type}} is neither {{ttb|s}} nor {{ttb|?s}}, | ||
− | * {{ | + | * {{box|{{tti|underlying_}}{{c/core|.set_debug_format()}}}} is a valid expression, and |
* there is no {{spar|range-underlying-spec}}. | * there is no {{spar|range-underlying-spec}}. | ||
Line 103: | Line 144: | ||
}} | }} | ||
− | {{member | 1={{small|std::range_formatter::}}format |2= | + | {{member|1={{small|std::range_formatter::}}format|2= |
{{dcl begin}} | {{dcl begin}} | ||
− | {{dcl | 1= | + | {{dcl|1= |
template< ranges::input_range R, class FormatContext > | template< ranges::input_range R, class FormatContext > | ||
requires std::formattable<ranges::range_reference_t<R>, CharT> && | requires std::formattable<ranges::range_reference_t<R>, CharT> && | ||
std::same_as<std::remove_cvref_t<ranges::range_reference_t<R>>, T> | std::same_as<std::remove_cvref_t<ranges::range_reference_t<R>>, T> | ||
− | auto format( R&& r, FormatContext& ctx ) const -> | + | auto format( R&& r, FormatContext& ctx ) const -> FormatContext::iterator; |
}} | }} | ||
{{dcl end}} | {{dcl end}} | ||
Line 116: | Line 157: | ||
Otherwise, it writes the following into {{c|ctx.out()}} as specified by {{spar|range-format-spec}}, in order: | Otherwise, it writes the following into {{c|ctx.out()}} as specified by {{spar|range-format-spec}}, in order: | ||
− | * {{tti|opening-bracket_}}, | + | * {{box|{{tti|opening-bracket_}}}}, |
− | * for each formattable element {{ | + | * for each formattable element {{c|e}} of the range {{c|r}}: |
− | :* the result of writing {{ | + | :* the result of writing {{c|e}} via {{tti|underlying_}}, and |
− | :* {{tti|separator_}}, unless {{ | + | :* {{box|{{tti|separator_}}}}, unless {{c|e}} is the last element of {{c|r}}, and |
− | * {{tti| | + | * {{box|{{tti|closing-bracket_}}}}. |
Returns an iterator past the end of the output range. | Returns an iterator past the end of the output range. | ||
}} | }} | ||
+ | |||
+ | ===Defect reports=== | ||
+ | {{dr list begin}} | ||
+ | {{dr list item|wg=lwg|dr=3892|std=c++23|before=the formatting of nested ranges was incorrect|after=corrected}} | ||
+ | {{dr list end}} | ||
===See also=== | ===See also=== | ||
Line 129: | Line 175: | ||
{{dsc inc|cpp/utility/format/dsc formatter}} | {{dsc inc|cpp/utility/format/dsc formatter}} | ||
{{dsc end}} | {{dsc end}} | ||
+ | |||
+ | {{langlinks|es|ja|ru|zh}} |
Latest revision as of 14:21, 11 September 2024
Defined in header <format>
|
||
template< class T, class CharT = char > requires std::same_as<std::remove_cvref_t<T>, T> && std::formattable<T, CharT> |
(since C++23) | |
The std::range_formatter
is a helper class template for implementing std::formatter specializations for range types.
Contents |
[edit] Range format specification
The syntax of range-format-spec is:
range-fill-and-align (optional) width (optional) n (optional) range-type (optional) range-underlying-spec (optional)
|
|||||||||
The range-fill-and-align is interpreted the same way as a fill-and-align except that the fill in range-fill-and-align is any character other than {
, }
, or :
.
The width is described in standard format width specification.
The n
option causes the range to be formatted without the opening and closing brackets.
assert(std::format("{}", views::iota(1, 5)) == "[1, 2, 3, 4]"); assert(std::format("{:n}", views::iota(1, 5)) == "1, 2, 3, 4");
The format-spec in a range-underlying-spec (its syntax is equivalent to :
format-spec), if any, is interpreted by the range element formatter std::formatter<T, CharT>
.
std::array ints{12, 10, 15, 14}; assert(std::format("{}", ints) == "[12, 10, 15, 14]"); assert(std::format("{::X}", ints) == "[C, A, F, E]"); assert(std::format("{:n:_^4}", ints) == "_12_, _10_, _15_, _14_");
The range-type changes the way a range is formatted, with certain options only valid with certain argument types.
The available range presentation types are:
-
m
: Indicates that the opening bracket should be "{", the closing bracket should be "}", the separator should be ", ", and each range element should be formatted as ifm
were specified for its tuple-type (in tuple-format-spec).
- If
m
is chosen as the range-type, the program is ill-formed unlessT
is either a specialization of:
- std::pair, or
- std::tuple such that std::tuple_size_v<T> == 2 is true.
- If
std::array char_pairs { std::pair{'A', 5}, std::pair{'B', 10}, std::pair{'C', 12} }; assert(std::format("{}", char_pairs) == "[('A', 5), ('B', 10), ('C', 12)]"); assert(std::format("{:m}", char_pairs) == "{'A': 5, 'B': 10, 'C': 12}");
-
s
: Indicates that the range should be formatted as a string. -
?s
: Indicates that the range should be formatted as an escaped string.
- If
s
or?s
is chosen as the range-type, bothn
option and range-underlying-spec should not be included in the format specifier, and - the program is ill-formed unless
T
isCharT
.
- If
std::array star{'S', 'T', 'A', 'R'}; assert(std::format("{}", star) == "['S', 'T', 'A', 'R']"); assert(std::format("{:s}", star) == "STAR"); assert(std::format("{:?s}", star) == "\"STAR\"");
[edit] Member objects
Member name | Definition |
underlying_ (private)
|
the underlying formatter of type std::formatter<T, CharT> (exposition-only member object*) |
separator_ (private)
|
a string representing the separator of the range formatted result. The default separator is ", ". (exposition-only member object*) |
opening-bracket_ (private)
|
a string representing the opening bracket of the range formatted result. The default opening bracket is "[". (exposition-only member object*) |
closing-bracket_ (private)
|
a string representing the closing bracket of the range formatted result. The default closing bracket is "]". (exposition-only member object*) |
[edit] Member functions
set_separator |
sets a specified separator for the range formatted result (public member function) |
set_brackets |
sets a specified opening and closing brackets for the range formatted result (public member function) |
underlying |
returns the underlying formatter (public member function) |
parse |
parses the format specifier as specified by range-format-spec (public member function) |
format |
writes the range formatted output as specified by range-format-spec (public member function) |
std::range_formatter::set_separator
constexpr void set_separator( std::basic_string_view<CharT> sep ) noexcept; |
||
Assigns sep to separator_
.
std::range_formatter::set_brackets
constexpr void set_brackets( std::basic_string_view<CharT> opening, std::basic_string_view<CharT> closing ) noexcept; |
||
Assigns opening and closing to opening-bracket_
and closing-bracket_
, respectively.
std::range_formatter::underlying
constexpr std::formatter<T, CharT>& underlying(); |
(1) | |
constexpr const std::formatter<T, CharT>& underlying() const; |
(2) | |
Returns underlying_
(the underlying formatter).
std::range_formatter::parse
template< class ParseContext > constexpr auto parse( ParseContext& ctx ) -> ParseContext::iterator; |
||
Parses the format specifiers as a range-format-spec and stores the parsed specifiers in the current object.
Calls underlying_
.parse(ctx) to parse format-spec in range-format-spec or, if the latter is not present, an empty format-spec.
If range-type or the n
option is present, the values of opening-bracket_
, closing-bracket_
, and separator_
are modified as required.
It calls underlying_
.set_debug_format() if:
- the range-type is neither
s
nor?s
, -
underlying_
.set_debug_format() is a valid expression, and - there is no range-underlying-spec.
Returns an iterator past the end of the range-format-spec.
std::range_formatter::format
template< ranges::input_range R, class FormatContext > requires std::formattable<ranges::range_reference_t<R>, CharT> && |
||
If the range-type was either s
or ?s
, it writes the formatted std::basic_string<CharT>(std::from_range, r) as a string or an escaped string, respectively, into ctx.out().
Otherwise, it writes the following into ctx.out() as specified by range-format-spec, in order:
-
opening-bracket_
, - for each formattable element e of the range r:
- the result of writing e via
underlying_
, and -
separator_
, unless e is the last element of r, and
- the result of writing e via
-
closing-bracket_
.
Returns an iterator past the end of the output range.
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
LWG 3892 | C++23 | the formatting of nested ranges was incorrect | corrected |
[edit] See also
(C++20) |
defines formatting rules for a given type (class template) |