How can I use C++20 std::format?
Asked Answered
D

2

32

C++20 introduces std::format. What are the advantages over printf or std::cout? How can I use it and someone give an example of it?

Doublebank answered 25/11, 2019 at 2:14 Comment(2)
Which part of the description on cppreference.com you are unclear about, and what, specifically, about that description that you have a question about?Nessim
You are not really comparing analogues things here. std::format fulfills the role of std::ostringstream and std::sprintf rather than std::cout and printf.Yancy
C
32

What are the advantages over printf

Type safety. For printf, the programmer must carefully match the format specifier to the type of the argument. If they make a mistake, the behaviour of the program is undefined. This is a very common source of bugs, especially for beginners.

To be fair, decent compilers diagnose these mistakes as long as a constant format string is used, as long as the programmer has remembered / knows how to enable the warnings. Regardless, it is much more convenient, and safer to use template argument deduction to choose the formatted type automatically.

Furthermore, there is no way to extend printf to support printing class types.

or std::cout

Stream manipulators are quite cumbersome and verbose, and have inconsistent behaviour. Some manipulators are "sticky", affecting all subsequent insertions, while others only affect only a single insertion.

The lack of separation between the format and the arguments in the iostream API arguably makes it harder to comprehend the (intended) result.

How can I use it

Either wait for your compiler / standard library implementation to support it. Or if don't want to wait, use the original non-standard version instead. Then follow the documentation.

The normative specification is the C++ standard. There are also websites that present the standard, including this library in a more convenient format. Another good source of information is the standard proposal. The repo for the non-standard version (linked in previous paragraph) also has tons of documentation, although there will be differences to what will be in the standard.

and someone give an example of it?

Here you go (adapted from the documentation of libfmt):

std::string s = std::format("I'd rather be {1} than {0}.", "right", "happy");
Clemmy answered 25/11, 2019 at 2:54 Comment(3)
"The lack of separation between the format and the arguments in the iostream API" also frustrates efforts to support multiple languages (where nouns and verbs may swap locations, for example).Katabolism
@Jeff: as an aside, that was solved long ago. When making messages that may need internationalisation, never do something like format("The {} {} is broken", color, furniture) since it's "red table" in English but the differently ordered table rouge in French. Instead, opt for something like format("The object specified by P1, of color P2 is broken (P1={}, P2={})", furniture, color). The only language-specific bit that needs to change if the totally self-contained format string, which can be tailored to each language. Order is otherwise irrelevant in the variable stuff.Vivanvivarium
glibc allows setting up custom specifiers to print custom types. It is a C function, and was intended to be used with C data types, but since pointers are just pointers, you could trick the function to believe that a pointer to a class is a pointer to void (you'd need to have the ABI on your side, and use glibc, though). It's curious that nobody mentioned speed, which is its main advantage, IMHO.Shwalb
V
22

C++20's std::format is primarily just the inclusion of the fmt library that many people are already using (we use it as part of the spdlog logging framework).

So, if you want to use it, you can just download fmt.

As for advantages, it has the type-safety of streams but without the verbosity (legacy-C printf is concise but neither type-safe nor extensible). Here's an example (slightly modified) from our own code base:

std::string idStr = fmt::format("prefix.{:05d}.suffix", id);

which would have otherwise required the rather less than concise standard C++:

std::string idStr;
{
    std::stringstream ss;
    ss << "prefix." << std::setfill('0') << std::setw(5) << id << ".suffix";
    idStr = ss.str();
}
Vivanvivarium answered 25/11, 2019 at 2:44 Comment(6)
To me the format version also conveys better how the output will look.Spiccato
Actually, one could imagine (and write) a format manipulator like ss << "prefix." << std::format(":05d") << id << ".suffix";, effectively bundeling the complete format for the next output item in one manipulator. (Not sure whether this could be done statically type-safe (more type-safe than printf)).Spiccato
@Peter, concur with your first comment (at least). This is also why I like Python f-strings, even the variables being printed are in the correct positions: f'prefix.{id:05d}.suffix'.Vivanvivarium
Thanks for the edit, @uneven_mark, I'd be quite happy if C++23 just killed off strstream and automatically treated std::sstream and std::strstream as std::stringstream. That would stop me from having to look it up every time I'm required to use it (in scenarios where I don't have fmt of course). Even now, I'm not certain I've put those things in the right place in this comment :-)Vivanvivarium
I wasn't even aware of std::strstream. I have never seen it used, but apparently it has been deprecated since before I started programming. I didn't even know you could have something deprecated in the first standard iteration. Looking at the interface, I think I am lucky not having had to use it.Yancy
@uneven_mark sidenote: Another feature of standard C++ that was deprecated from conception (of the standard) is implicit conversion from string literal to non-const char*. That conversion was removed in C++11.Clemmy

© 2022 - 2024 — McMap. All rights reserved.