Section: 28.5.6.1 [formatter.requirements] Status: NAD Submitter: Mark de Wever Opened: 2022-08-28 Last modified: 2023-06-16
Priority: 3
View all other issues in [formatter.requirements].
View all issues with NAD status.
Discussion:
A format string 28.5.2.1 [format.string.general]/1 has an optional format-specifier replacement field. If this field is not present, the current wording makes it clear the intention is to call parse when a formatting argument use a handle class 28.5.8.1 [format.arg]/19
Effects: Initializes
ptr_
withaddressof(val)
andformat_
with[](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) { typename Context::template formatter_type<TD> f; parse_ctx.advance_to(f.parse(parse_ctx)); format_ctx.advance_to(f.format(*const_cast<TQ*>(static_cast<const TD*>(ptr)), format_ctx)); }
For other types it is implied by [tab:formatter.basic]
Formats
u
according to the specifiers stored in*this
, writes the output tofc.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 valuen
of typesize_t
, and the range[pc.begin(), pc.end())
from the last call tof.parse(pc)
.
(Similar wording is used in [tab:formatter])
Before theparse
function is called it is known whether or not a
format-spec is present. It seems wasteful to call a function that is
known not to parse anything. Therefore I propose to make the call
optional.
This change is only observable for formatter specializations using the
handle
class, for the formatter specializations in
28.5.6.4 [format.formatter.spec] the change has no observable effect.
[2022-10-12; Reflector poll]
Set status to "LEWG" and priority to 3 after reflector poll.
Several votes for NAD as this would be a design change requiring a paper.
Dissenting opinions: "Different handling of empty and default specs is entirely
incidental and not a design feature. Moreover, built-in formatters treat these
cases identically so it's a good idea to make this explicit."
"Don't need a full paper to clarify the meaning of "don't call member
parse
if there's no format spec".
If LEWG agree the direction PR can be polished."
[Issaquah 2023-02-07; LWG]
Will be resolved differently by P2733.
[Varna 2023-06-16; Status changed: LEWG → NAD.]
Resolved differently by LWG 3892.
Proposed resolution:
This wording is relative to N4917.
Modify 28.5.6.1 [formatter.requirements] as indicated:
-3- Given character type
[…]charT
, output iterator typeOut
, and formatting argument typeT
, in Table 74 and Table 75:pc.begin()
points to the beginning of the format-spec (28.5.2 [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() == '}'
.
Modify BasicFormatter requirements [tab:formatter.basic] as indicated:
Table 74: BasicFormatter requirements [tab:formatter.basic] Expression Return type Requirement …
f.format(u, fc)
FC::iterator
Formats u
according to the specifiers stored in
*this
, writes the output tofc.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 valuen
of typesize_t
, and
the range[pc.begin(), pc.end())
from the last
call tof.parse(pc)
. When the format-spec
(28.5.2 [format.string]) is not present or empty
the call tof.parse(pc)
is omitted.
Modify Formatter requirements [tab:formatter] as indicated:
Table 75: Formatter requirements [tab:formatter] Expression Return type Requirement f.format(t, fc)
FC::iterator
Formats t
according to the specifiers stored in
*this
, writes the output tofc.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 valuen
of typesize_t
, and
the range[pc.begin(), pc.end())
from the last
call tof.parse(pc)
. When the format-spec
(28.5.2 [format.string]) is not present or empty
the call tof.parse(pc)
is omitted.…
Modify 28.5.8.1 [format.arg] as indicated:
[…]
-16- The classhandle
allows formatting an object of a user-defined type.namespace std { template<class Context> class basic_format_arg<Context>::handle { const void* ptr_; // exposition only void (*format_)(basic_format_parse_context<char_type>&*, Context&, const void*); // exposition only template<class T> explicit handle(T&& val) noexcept; // exposition only friend class basic_format_arg<Context>; // exposition only public: void format(basic_format_parse_context<char_type>&, Context& ctx) const; }; }template<class T> explicit handle(T&& val) noexcept;-17- […]
-18- […] -19- Effects: Initializesptr_
withaddressof(val)
andformat_
with[](basic_format_parse_context<char_type>&* parse_ctx, Context& format_ctx, const void* ptr) { typename Context::template formatter_type<TD> f; if (parse_ctx) parse_ctx.->advance_to(f.parse(*parse_ctx)); format_ctx.advance_to(f.format(*const_cast<TQ*>(static_cast<const TD*>(ptr)), format_ctx)); }void format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx) const;-20- Effects: If the format-spec (28.5.2 [format.string]) is not present or empty, equivalent to:
format_(nullptr, format_ctx, ptr_);otherwise, e
Equivalent to:format_(addressof(parse_ctx), format_ctx, ptr_);