Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
josuttis_nm_c20_the_complete_guide.pdf
Скачиваний:
44
Добавлен:
27.03.2023
Размер:
5.85 Mб
Скачать

322

Chapter 10: Formatted Output

std::cout << mem.data() << '\n'; has the following output:

1234

10.1.3 Using std::format_to()

The formatting library also provides std::format_to(), which writes an unlimited number of characters for formatted output. Using this function for limited memory is a risk because if the value needs too much memory, you have undefined behavior. However, by using an output stream buffer iterator, you can safely use it to write directly to a stream:

std::format_to(std::ostreambuf_iterator<char>{std::cout}, "String '{}' has {} chars\n", str, str.size());

In general, std::format_to() takes any output iterator for characters. For example, you can also use a back inserter to append the characters to a string:

std::string s; std::format_to(std::back_inserter(s),

"String '{}' has {} chars\n", str, str.size());

The helper function std::back_inserter() creates an object that calls push_back() for each character. Note that the implementation of std::format_to() can recognize that back insert iterators are passed and write multiple characters for certain containers at once so that we still have good performance.1

10.1.4 Using std::formatted_size()

To know in advance how many characters would be written by a formatted output (without writing any), you can use std::formatted_size(). For example:

auto sz = std::formatted_size("String '{}' has {} chars\n", str, str.size());

That would allow you to reserve enough memory or double check that the reserved memory fits.

10.2Performance of the Formatting Library

One reason to still use sprintf() was that it has a significantly better performance than using output string streams or std::to_string(). The formatting library was designed with the goal of doing this better. It should format at least as fast as sprintf() or even faster.

Current (draft) implementations show that an equal or even better performance is possible. Rough measurements show that

std::format() should be as fast as or even better than sprintf()

std::format_to() and std::format_to_n() should be even better

1 Thanks to Victor Zverovich for pointing this out.

10.2 Performance of the Formatting Library

323

The fact that compilers can usually check format strings at compile time does help a lot. It helps to avoid mistakes with formatting as well as to get a significantly better performance.

However, the performance ultimately depends on the quality of the implementation of the formatting libraries on your specific platform.2 Therefore, you should measure the performance yourself. The program format/formatperf.cpp might give you some hints abut the situation on your platform.

10.2.1 Using std::vformat() and vformat_to()

To support this goal, an important fix was adopted for the formatting library after C++20 was standardized (see http://wg21.link/p2216r3). With this fix, the functions std::format(), std::format_to(), and std::format_to_n() require that the format string is a compile-time value. You have to pass a string literal or a constexpr string. For example:

const char* fmt1 = "{}\n";

// runtime format string

std::cout << std::format(fmt1, 42);

// compile-time ERROR: runtime format

constexpr const char* fmt2 = "{}\n";

// compile-time format string

std::cout << std::format(fmt2, 42);

// OK

As a consequence, invalid format specifications become compile-time errors:

std::cout << std::format("{:7.2f}\n", 42);

// compile-time ERROR: invalid format

constexpr const char* fmt2 = "{:7.2f}\n";

// compile-time format string

std::cout << std::format(fmt2, 42);

// compile-time ERROR: invalid format

Of course, applications sometimes have to compute formatting details at runtime (such as computing the best width according to the passed values). In that case, you have to use std::vformat() or std::vformat_to() and pass all arguments with std::make_format_args() to these functions:

const char* fmt3 = "{} {}\n";

// runtime format

std::cout << std::vformat(fmt3, std::make_format_args(42, 1.7));

// OK

If a runtime format string is used and the formatting is not valid for the passed argument, the call throws a runtime error of type std::format_error:

const char* fmt4 = "{:7.2f}\n";

std::cout << std::vformat(fmt4, std::make_format_args(42)); // runtime ERROR: // throws std::format_error exception

2For example, while writing this book, for Visual C++, the option /utf-8 improves the formatting performance significantly.