June 13, 2020·4 min read·engineeringcppformattingiounicode

Formatted output should not require an intermediate string

P2093R0 proposes connecting C++ formatting directly to output. It also addresses Unicode terminals and keeps argument preparation separate from the shared output implementation.

Formatting a message and sending it to an output destination are related operations with different interfaces. A program that first creates a string and then inserts it into a stream must connect those interfaces itself. Victor Zverovich's P2093R0 proposes a direct formatted-output facility. The WG21 document index records this revision in the June mailing with a June 13 document date.

The proposal's opening comparison is concrete. Instead of formatting into a temporary string and passing that result into another output operation, a caller can supply the format and arguments to std::print. This is a proposed interface, not a facility already shipped as part of the standard library.

C++'s abstraction cost matters to me, particularly when a simple operation expands into several layers of formatting machinery. A direct interface is useful when it reduces incidental work while preserving a clear explanation of encoding and output behavior.

The destination should be part of the operation

The proposed call puts the message template and its arguments together.

std::print("Processed {} records\n", count);

This example uses the proposal's spelling and explicit newline. P2093R0 does not make print append a newline automatically. Its design also considers a separate println facility for that convention.

A single formatting interface improves the relationship between a message and the values placed into it. The text remains together, while argument expressions appear alongside it. A reader can inspect the message independently of a chain of stream insertions.

The direct interface also lets the implementation coordinate formatting with the destination. If the caller first produces a complete string, the output layer receives text whose formatting decisions have already happened. A combined operation can manage that process internally.

This does not imply that every implementation must avoid every buffer. Buffering can be useful for output. The distinction is that an application-facing temporary string is no longer required merely to connect two public interfaces.

For a logging wrapper, that reduces one reason to invent a separate formatting convention. The wrapper can describe its destination and error policy while relying on the underlying formatting model. It still needs to state when and where the output occurs.

Unicode depends on the output path

The proposal also addresses a terminal whose interpretation of output differs from the program's string-literal encoding. Correctly encoded bytes in the program do not establish that a terminal will display the intended characters. The output interface has to preserve their meaning across that boundary.

P2093R0 proposes a Unicode-aware path that uses an appropriate native terminal API when needed, while preserving byte output in other cases. Its distinction includes whether the destination is a terminal capable of Unicode display. This is more specific than declaring that one string encoding solves output everywhere.

The practical implication is that a terminal and a redirected file can require different treatment. A file normally needs the specified bytes. A terminal API may need a representation suited to displaying characters through the platform's supported interface.

That distinction belongs near the output operation because the destination determines which case applies. A formatting function that only returns a string cannot, by itself, know how a later caller will deliver it. Combining formatting and output gives the implementation the relevant destination information.

The destination distinction also prevents an accidental change to machine-readable output. A program whose output is redirected into another tool needs that tool to receive the agreed encoding. A display-oriented conversion applied indiscriminately would change those bytes. Locating the decision in an operation that knows the destination lets the specification describe both cases. The implementation can then preserve terminal display behavior without treating every output stream as a display device.

Applications still need an encoding contract for their own data. An output facility cannot infer the intended characters from arbitrary invalid or ambiguously encoded input. It can define how correctly described input reaches a known destination.

Templates can prepare arguments for shared code

The proposal describes a small variadic wrapper that prepares type-erased argument references for a non-variadic implementation. That lets the public function retain typed arguments while the larger formatting and output logic has a common implementation.

The separation is useful because every distinct argument pack can create a distinct template instantiation. If all output logic lives in that template, an implementation risks repeating substantial code for many combinations. A shared implementation gives the compiler less distinct machinery to emit at each call site.

Type erasure here has a specific purpose. The wrapper records enough information for the shared implementation to format the supplied values. It does not require the caller to discard type information before making the call.

The earlier text-formatting proposal P0645R10 describes the formatting facilities on which P2093 builds. Keeping that machinery common means the output proposal can focus on delivering the formatted result rather than defining an unrelated format language.

I find this division of implementation responsibility more informative than an unconditional zero-overhead claim. A typed interface still has argument-preparation work. The design aims to keep that work small and share the larger implementation where specialization is unnecessary.

One output call still needs an error contract

A direct interface can simplify the call site without removing output failure. Formatting can fail, and the destination can fail independently. A wrapper around the proposed facility needs to decide how those failures participate in the application's control flow.

This is especially relevant when output accompanies another operation. Printing a completion message does not establish that an earlier state change committed successfully. Conversely, failure to display the message does not necessarily undo that state change. The application needs to keep those outcomes distinct.

The proposed facility is valuable because it gives a common interface to a frequent operation. It connects formatting with a destination and gives encoding handling a defined place. The application can then state its own output policy without first constructing a temporary string solely to pass formatted text to another API.