This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of LEWG status.

### 3776. Avoid parsing format-spec if it is not present or empty

Section: 22.14.6.1 [formatter.requirements] Status: LEWG Submitter: Mark de Wever Opened: 2022-08-28 Last modified: 2022-11-01

Priority: 3

View all other issues in [formatter.requirements].

View all issues with LEWG status.

Discussion:

A format string 22.14.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 22.14.8.1 [format.arg]/19

> Effects: Initializes `ptr_` with `addressof(val)` and `format_` 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 to `fc.out()`, and returns an iterator past the end of the output range. The output shall only depend on `u`, `fc.locale()`, `fc.arg(n)` for any value `n` of type `size_t`, and the range `[pc.begin(), pc.end())` from the last call to `f.parse(pc)`.

(Similar wording is used in [tab:formatter])

Before the `parse` 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 22.14.6.3 [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."

Proposed resolution:

This wording is relative to N4917.

1. Modify 22.14.6.1 [formatter.requirements] as indicated:

   > -3- Given character type `charT`, output iterator type `Out`, and formatting argument type `T`, in Table 74 and Table 75:

   > […]

   > `pc.begin()` points to the beginning of the format-spec (22.14.2 [format.string]) of the replacement field being formatted in the format string. If format-spec is not present or empty then either `pc.begin() == pc.end()` or `*pc.begin() == '}'`.

2. 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 to `fc.out()`, and returns an iterator past the end of the output range. The output shall only depend on `u`, `fc.locale()`, `fc.arg(n)` for any value `n` of type `size_t`, and the range `[pc.begin(), pc.end())` from the last call to `f.parse(pc)`. When the format-spec (22.14.2 [format.string]) is not present or empty the call to `f.parse(pc)` is omitted.

3. 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 to `fc.out()`, and returns an iterator past the end of the output range. The output shall only depend on `t`, `fc.locale()`, `fc.arg(n)` for any value `n` of type `size_t`, and the range `[pc.begin(), pc.end())` from the last call to `f.parse(pc)`. When the format-spec (22.14.2 [format.string]) is not present or empty the call to `f.parse(pc)` is omitted.
   > `…`

4. Modify 22.14.8.1 [format.arg] as indicated:

   > […]

   > -16- The class `handle` 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: Initializes `ptr_` with `addressof(val)` and `format_` 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 (22.14.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_);