4042. std::print should permit an efficient implementation

Section: 31.7.10 [print.fun] Status: LEWG Submitter: Victor Zverovich Opened: 2024-01-20 Last modified: 2024-03-12

Priority: 3

View other active issues in [print.fun].

View all other issues in [print.fun].

View all issues with LEWG status.

Discussion:

std::print/std::vprint* is currently defined in terms of formatting into a temporary string, e.g. 31.7.10 [print.fun]:

void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);

Preconditions: stream is a valid pointer to an output C stream.

Effects: Writes the result of vformat(fmt, args) to stream.

Throws: Any exception thrown by the call to vformat (28.5.3 [format.err.report]). system_error if writing to stream fails. May throw bad_alloc.

This is done to make it clear that noninterleaved output is desired while keeping specification simple and portable.

Unfortunately, the current specification seems to prohibit a more efficient implementation that performs formatting directly into a stream buffer under a lock (flockfile/funlockfile in POSIX) like printf does. The difference can be observable in case of an I/O error that occurs before a custom formatter is called. In the (double buffered) implementation that directly follows the spec all formatters will be called, while in a more efficient (locking) implementation subsequent formatters may not be called.

The easiest fix, given in the current proposed resolution, is to say that some arguments may not be formatted in case of a write error. It might be a bit weird considering that the spec says that we construct a string first so an alternative resolution is to replace vformat with vformat_to info some unspecified buffer iterator and state noninterleaving requirement separately.

[2024-02-19; Feb 2024 mailing]

This would be resolved by P3107.

[2024-03-12; Reflector poll]

Set priority to 3 and status to LEWG after reflector poll in January 2024.

"This loses the guarantee that if the formatting throws then there's no output."

Proposed resolution:

This wording is relative to N4971.

  1. Modify 31.7.10 [print.fun] as indicated:

    void vprint_unicode(FILE* stream, string_view fmt, format_args args);
    

    -6- Preconditions: stream is a valid pointer to an output C stream.

    -7- Effects: The function initializes an automatic variable via

    string out = vformat(fmt, args);
    

    If stream refers to a terminal capable of displaying Unicode, writes out to the terminal using the native Unicode API; if out contains invalid code units, the behavior is undefined and implementations are encouraged to diagnose it. Otherwise writes out to stream unchanged. If the native Unicode API is used, the function flushes stream before writing out.

    If writing to the terminal or stream fails, some arguments in args may not be formatted.

    […]

    […]
    void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
    

    -11- Preconditions: stream is a valid pointer to an output C stream.

    -12- Effects: Writes the result of vformat(fmt, args) to stream. If writing to stream fails, some arguments in args may not be formatted.

    -13- Throws: […].