Printing Formatted Strings in C++20

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 iostream

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

Share This
      

"Books give you knowledge, and knowledge is power, and power corrupts, and corruption leads to crime, and crime doesn't pay, so if you read books you'll go broke." --Calvin

.

The lawyers made me say it: Some links may be affiliate links and I may be compensated if you make a purchase after clicking on them.