This posts describes an interim replacement for the missing formatted print()
function in C++20.
The C++20 standard has been approved and the various major compilers are beginning to support the new features.
There are a lot of great new features in C++20 but the one feature I’ve been most looking forward to is text formatting, as specified in proposal P0645, which brings Python-like string formatting to C++.
Why this is important
Up to now, C++ has provided two ways to format strings.
The original printf
family of functions use a formatter inherited from C. It’s a bit cryptic, provides no type-checking, but it’s small, fast, and extremely efficient.
The iostream
library formats text using manipulators. While iostream
provides some benefits over printf
, most notably it provides type safety, these manipulators are awkward, prone to error, and generally ignored in favor of custom code or third-party libraries.
Many of us in the C++ community have long hoped for a standard alternative with both the speed and efficiency of printf
and the type safety of i
ostream
.
An elegant solution
C++20 provides a new text formatting library that may be used as a safe and extensible alternative to the printf
family of functions. The new std::format
library is heavily inspired by the Python str.format
method.
string message = format("The answer is {}.", 42);
format
provides a rich set of formatting options that exceeds even that of printf
. It provides positional arguments and is even extensible to support more values and classes.
time_t t = time(nullptr);
string s = format("The date is {0:%Y-%m-%d}.\n", *localtime(&t));
As of the time of writing, there is only one major C++ compiler that supports the new format
library, Microsoft Visual C++.
But there is another problem, which is the point of this article.
Missing print()
function
The original draft of the C++20 standard included a function, called print()
, which would serve the purpose of directly sending formatted strings to the console or any file or output stream, like printf()
or iostream
. Many of us were disappointed to find that the print()
function was not included in the final C++20 standard. The promised print()
function would allow print statements with all the functionality of the format library.
print("Hello, {}\n", "everyone everywhere!");
The good news is that print()
is slated to appear in the C++23 update. (See P2093.) But in the meantime, we’re left with a partially functional format
in the C++20 library.
While writing my next book on the C++ STL, I wanted to be able to use the new format library, but having to use std::cout
for all the output was proving as cumbersome as ever. So I took matters into my own hands and wrote a print()
function for the format
library. As it turns out, it was pretty simple.
My replacement print()
function
Using the P2093 proposal as a guide, I wrote a very simple print()
function which works with the existing C++20 format
library (or the excellent open source libfmt library).
The main format()
function is a variadic template function which provides type safety, along with performance and efficiency advantages over iostream
. The format
library also provides vformat()
, a type-erased interface that prevents generating multiple instances of a complex template function for each combination of arguments. I use this to pass a string to the fputs()
library function to print to stdout
. Here’s my code for a simple print()
function:
template<typename... Args>
void print(const string_view str_fmt, Args&&... args) {
fputs(vformat(str_fmt, make_format_args(args...)).c_str(),
stdout);
}
The make_format_args()
function creates a type-erased parameter stack to pass to vformat()
, which does the heavy lifting of formatting the string. This is how the format()
library operates so efficiently.
I’ve included overloads for both FILE*
and iostream
destinations, so you can do,
print(stderr, "the error is {}\n", errno);
… and,
print(std::cerr, "the error is {}\n", errno);
The default destination is stdout
.
My print()
function is all in a header file. The file is available here.