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

### 3340. Formatting functions should throw on argument/format string mismatch in §[format.functions]

Section: 20.20.5 [format.functions] Status: C++20 Submitter: Great Britain Opened: 2019-11-17 Last modified: 2021-02-25

Priority: Not Prioritized

View all other issues in [format.functions].

View all issues with C++20 status.

Discussion:

Addresses GB 229

Formatting functions don't allow throwing on incorrect arguments. `std::format` is only allowed to throw if `fmt` is not a format string, but the intention is it also throws for errors during formatting, e.g. there are fewer arguments than required by the format string.

Proposed change:

Allow exceptions even when the format string is valid. Possibly state the Effects: more precisely.

Victor Zverovich:

LEWG approved resolution of this NB comment as an LWG issue. Previous resolution [SUPERSEDED]:

> This wording is relative to N4835.

> > [Drafting Note: Depending on whether LWG 3336's wording has been accepted when this issue's wording has been accepted, two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

> Option A (LWG 3336 has been accepted)

> 1. Change 20.20.2.1 [format.string.general] as follows:

>    > -1- A format string for arguments `args` is a (possibly empty) sequence of replacement fields, escape sequences, and characters other than `{` and `}`. […]

>    > -2- The arg-id field specifies the index of the argument in `args` whose value is to be formatted and inserted into the output instead of the replacement field. If there is no argument with the index arg-id in `args`, the string is not a format string. The optional format-specifier field explicitly specifies a format for the replacement value.

>    > […]

>    > -5- The format-spec field contains format specifications that define how the value should be presented. Each type can define its own interpretation of the format-spec field. If format-spec doesn't conform to the format specifications for the argument in `args` referred to by arg-id, the string is not a format string. […]

> 2. Before 20.20.5 [format.functions] insert a new sub-clause as indicated:

>    > 20.20.? Error reporting [format.err.report]

>    > -?- Formatting functions throw exceptions to report formatting and other errors. They throw `format_error` if an argument `fmt` is passed that is not a format string for arguments `args` and propagate exceptions thrown by `formatter` specializations and iterator operations. Failure to allocate storage is reported by throwing an exception as described in 16.4.6.13 [res.on.exception.handling].

> 3. Modify 20.20.5 [format.functions] as indicated:

>    >     string vformat(string_view fmt, format_args args);
>    >     wstring vformat(wstring_view fmt, wformat_args args);
>    >     string vformat(const locale& loc, string_view fmt, format_args args);
>    >     wstring vformat(const locale& loc, wstring_view fmt, wformat_args args);

>    > > -6- […]

>    > > -7- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].

>    > […]

>    >     template<class Out>
>    >       Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args);
>    >     template<class Out>
>    >       Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args);
>    >     template<class Out>
>    >       Out vformat_to(Out out, const locale& loc, string_view fmt,
>    >                      format_args_t<Out, char> args);
>    >     template<class Out>
>    >       Out vformat_to(Out out, const locale& loc, wstring_view fmt,
>    >                      format_args_t<Out, wchar_t> args);

>    > > […]

>    > > -15- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].

>    > […]

>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           string_view fmt, const Args&... args);
>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           wstring_view fmt, const Args&... args);
>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           const locale& loc, string_view fmt,
>    >                                           const Args&... args);
>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           const locale& loc, wstring_view fmt,
>    >                                           const Args&... args);

>    > > […]

>    > > -21- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].

>    > […]

>    >     template<class... Args>
>    >       size_t formatted_size(string_view fmt, const Args&... args);
>    >     template<class... Args>
>    >       size_t formatted_size(wstring_view fmt, const Args&... args);
>    >     template<class... Args>
>    >       size_t formatted_size(const locale& loc, string_view fmt, const Args&... args);
>    >     template<class... Args>
>    >       size_t formatted_size(const locale& loc, wstring_view fmt, const Args&... args);

>    > > […]

>    > > -25- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].

> Option B (LWG 3336 has not been accepted)

> 1. Change 20.20.2.1 [format.string.general] as follows:

>    > -1- A format string for arguments `args` is a (possibly empty) sequence of replacement fields, escape sequences, and characters other than `{` and `}`. […]

>    > -2- The arg-id field specifies the index of the argument in `args` whose value is to be formatted and inserted into the output instead of the replacement field. If there is no argument with the index arg-id in `args`, the string is not a format string. The optional format-specifier field explicitly specifies a format for the replacement value.

>    > […]

>    > -5- The format-spec field contains format specifications that define how the value should be presented. Each type can define its own interpretation of the format-spec field. If format-spec doesn't conform to the format specifications for the argument in `args` referred to by arg-id, the string is not a format string. […]

> 2. Modify 20.20.5 [format.functions] as indicated:

>    >     string vformat(string_view fmt, format_args args);
>    >     wstring vformat(wstring_view fmt, wformat_args args);
>    >     string vformat(const locale& loc, string_view fmt, format_args args);
>    >     wstring vformat(const locale& loc, wstring_view fmt, wformat_args args);

>    > > -6- […]

>    > > -7- Throws: `format_error` if `fmt` is not a format string for `args`.

>    > […]

>    >     template<class Out>
>    >       Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args);
>    >     template<class Out>
>    >       Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args);
>    >     template<class Out>
>    >       Out vformat_to(Out out, const locale& loc, string_view fmt,
>    >                      format_args_t<Out, char> args);
>    >     template<class Out>
>    >       Out vformat_to(Out out, const locale& loc, wstring_view fmt,
>    >                      format_args_t<Out, wchar_t> args);

>    > > […]

>    > > -15- Throws: `format_error` if `fmt` is not a format string for `args`.

>    > […]

>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           string_view fmt, const Args&... args);
>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           wstring_view fmt, const Args&... args);
>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           const locale& loc, string_view fmt,
>    >                                           const Args&... args);
>    >     template<class Out, class... Args>
>    >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
>    >                                           const locale& loc, wstring_view fmt,
>    >                                           const Args&... args);

>    > > […]

>    > > -21- Throws: `format_error` if `fmt` is not a format string for `args`.

>    > […]

>    >     template<class... Args>
>    >       size_t formatted_size(string_view fmt, const Args&... args);
>    >     template<class... Args>
>    >       size_t formatted_size(wstring_view fmt, const Args&... args);
>    >     template<class... Args>
>    >       size_t formatted_size(const locale& loc, string_view fmt, const Args&... args);
>    >     template<class... Args>
>    >       size_t formatted_size(const locale& loc, wstring_view fmt, const Args&... args);

>    > > […]

>    > > -25- Throws: `format_error` if `fmt` is not a format string for `args`.

[2020-02-12, Prague; LWG discussion]

Option A is the only one we look at to resolve LWG 3336 as well. During the discussions some wording refinements have been suggested that are integrated below.

Proposed resolution:

This wording is relative to N4849.

1. Change 20.20.2.1 [format.string.general] as follows:

   > -1- A format string for arguments `args` is a (possibly empty) sequence of replacement fields, escape sequences, and characters other than `{` and `}`. […]

   > -2- The arg-id field specifies the index of the argument in `args` whose value is to be formatted and inserted into the output instead of the replacement field. If there is no argument with the index arg-id in `args`, the string is not a format string for `args`. The optional format-specifier field explicitly specifies a format for the replacement value.

   > […]

   > -5- The format-spec field contains format specifications that define how the value should be presented. Each type can define its own interpretation of the format-spec field. If format-spec does not conform to the format specifications for the argument type referred to by arg-id, the string is not a format string for `args`. […]

2. Before 20.20.5 [format.functions] insert a new sub-clause as indicated:

   > 20.20.? Error reporting [format.err.report]

   > -?- Formatting functions throw `format_error` if an argument `fmt` is passed that is not a format string for `args`. They propagate exceptions thrown by operations of `formatter` specializations and iterators. Failure to allocate storage is reported by throwing an exception as described in 16.4.6.13 [res.on.exception.handling].

3. Modify 20.20.5 [format.functions] as indicated:

   >     string vformat(string_view fmt, format_args args);
   >     wstring vformat(wstring_view fmt, wformat_args args);
   >     string vformat(const locale& loc, string_view fmt, format_args args);
   >     wstring vformat(const locale& loc, wstring_view fmt, wformat_args args);

   > > -6- […]

   > > -7- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].

   > […]

   >     template<class Out>
   >       Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args);
   >     template<class Out>
   >       Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args);
   >     template<class Out>
   >       Out vformat_to(Out out, const locale& loc, string_view fmt,
   >                      format_args_t<Out, char> args);
   >     template<class Out>
   >       Out vformat_to(Out out, const locale& loc, wstring_view fmt,
   >                      format_args_t<Out, wchar_t> args);

   > > […]

   > > -15- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].

   > […]

   >     template<class Out, class... Args>
   >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
   >                                           string_view fmt, const Args&... args);
   >     template<class Out, class... Args>
   >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
   >                                           wstring_view fmt, const Args&... args);
   >     template<class Out, class... Args>
   >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
   >                                           const locale& loc, string_view fmt,
   >                                           const Args&... args);
   >     template<class Out, class... Args>
   >       format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
   >                                           const locale& loc, wstring_view fmt,
   >                                           const Args&... args);

   > > […]

   > > -21- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].

   > […]

   >     template<class... Args>
   >       size_t formatted_size(string_view fmt, const Args&... args);
   >     template<class... Args>
   >       size_t formatted_size(wstring_view fmt, const Args&... args);
   >     template<class... Args>
   >       size_t formatted_size(const locale& loc, string_view fmt, const Args&... args);
   >     template<class... Args>
   >       size_t formatted_size(const locale& loc, wstring_view fmt, const Args&... args);

   > > […]

   > > -25- Throws: `format_error` if `fmt` is not a format string As specified in 20.20.3 [format.err.report].