Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying Modern C++ Programming Cookbook
  • Table Of Contents Toc
Modern C++ Programming Cookbook

Modern C++ Programming Cookbook - Second Edition

By : Marius Bancila
4.7 (12)
close
close
Modern C++ Programming Cookbook

Modern C++ Programming Cookbook

4.7 (12)
By: Marius Bancila

Overview of this book

C++ has come a long way to be one of the most widely used general-purpose languages that is fast, efficient, and high-performance at its core. The updated second edition of Modern C++ Programming Cookbook addresses the latest features of C++20, such as modules, concepts, coroutines, and the many additions to the standard library, including ranges and text formatting. The book is organized in the form of practical recipes covering a wide range of problems faced by modern developers. The book also delves into the details of all the core concepts in modern C++ programming, such as functions and classes, iterators and algorithms, streams and the file system, threading and concurrency, smart pointers and move semantics, and many others. It goes into the performance aspects of programming in depth, teaching developers how to write fast and lean code with the help of best practices. Furthermore, the book explores useful patterns and delves into the implementation of many idioms, including pimpl, named parameter, and attorney-client, teaching techniques such as avoiding repetition with the factory pattern. There is also a chapter dedicated to unit testing, where you are introduced to three of the most widely used libraries for C++: Boost.Test, Google Test, and Catch2. By the end of the book, you will be able to effectively leverage the features and techniques of C++11/14/17/20 programming to enhance the performance, scalability, and efficiency of your applications.
Table of Contents (16 chapters)
close
close
13
Bibliography
14
Other Books You May Enjoy
15
Index

Formatting text with std::format

The C++ language has two ways of formatting text: the printf family of functions and the I/O streams library. The printf functions are inherited from C and provide a separation of the formatting text and the arguments. The streams library provides safety and extensibility and is usually recommended over printf functions, but is, in general, slower. The C++20 standard proposes a new formatting library alternative for output formatting, which is similar in form to printf but safe and extensible and is intended to complement the existing streams library. In this recipe, we will learn how to use the new functionalities instead of the printf functions or the streams library.

Getting ready

The new formatting library is available in the header <format>. You must include this header for the following samples to work.

How to do it...

The std::format() function formats its arguments according to the provided formatting string. You can use it as follows:

  • Provide empty replacement fields, represented by {}, in the format string for each argument:
    auto text = std::format("{} is {}", "John", 42);
    
  • Specify the 0-based index of each argument in the argument list inside the replacement field, such as {0}, {1}, and so on. The order of the arguments is not important, but the index must be valid:
    auto text = std::format("{0} is {1}", "John", 42);
    
  • Control the output text with format specifiers provided in the replacement field after a colon (:). For basic and string types, this is a standard format specification. For chrono types, this is a chrono format specification:
    auto text = std::format("{0} hex is {0:08X}", 42);
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    auto text = std::format("Today is {:%Y-%m-%d}", *std::localtime(&time));
    

You can also write the arguments in an out format using an iterator with either std::format_to() or std::format_to_n(), as follows:

  • Write to a buffer, such as an std::string or std::vector<char>, using std::format_n() and using the std::back_inserter() helper function:
    std::vector<char> buf;
    std::format_to(std::back_inserter(buf), "{} is {}", "John", 42);
    
  • Use std::formatted_size() to retrieve the number of characters necessary to store the formatted representation of the arguments:
    auto size = std::formatted_size("{} is {}", "John", 42);
    std::vector<char> buf(size);
    std::format_to(buf.data(), "{} is {}", "John", 42);
    
  • To limit the number of characters written to the output buffer, you can use std::format_to_n(), which is similar to std::format_to() but writes, at most, n characters:
    char buf[100];
    auto result = std::format_to_n(buf, sizeof(buf), "{} is {}", "John", 42);
    

How it works...

The std::format() function has multiple overloads. You can specify the format string either as a string view or a wide string view, with the function returning either an std::string or an std::wstring. You can also specify, as the first argument, an std::locale, which is used for locale-specific formatting. The function overloads are all variadic function templates, which means you can specify any number of arguments after the format.

The format string consists of ordinary characters, replacement fields, and escape sequences. The escape sequences are {{ and }} and are replaced with { and } in the output. A replacement field is provided within curly brackets {}. It can optionally contain a non-negative number, representing the 0-based index of the argument to be formatted, and a colon (:), followed by a format specifier. If the format specifier is invalid, an exception of the type std::format_error is thrown.

In a similar manner, std::format_to() has multiple overloads, just like std::format(). The difference between these two is that std::format_to() always takes an iterator to the output buffer as the first argument and returns an iterator past the end of the output range (and not a string as std::format() does). On the other hand, std::format_to_n() has one more parameter than std::format_to(). Its second parameter is a number representing the maximum number of characters to be written to the buffer.

The following listing shows the signature of the simplest overload of each of these three function templates:

template<class... Args>
std::string format(std::string_view fmt, const Args&... args);
template<class OutputIt, class... Args>
OutputIt format_to(OutputIt out,
                   std::string_view fmt, const Args&... args);
template<class OutputIt, class... Args>
std::format_to_n_result<OutputIt>
format_to_n(OutputIt out, std::iter_difference_t<OutputIt> n,
            std::string_view fmt, const Args&... args);

When you provide the format string, you can supply argument identifiers (their 0-based index) or omit them. However, it is illegal to use both. If the indexes are omitted in the replacement fields, the arguments are processed in the provided order, and the number of replacement fields must not be greater than the number of supplied arguments. If indexes are provided, they must be valid for the format string to be valid.

When a format specification is used, then:

  • For basic types and string types, it is considered to be a standard format specification.
  • For chrono types, it is considered to be a chrono format specification.
  • For user-defined types, it is defined by a user-defined specialization of the std::formatter class for the desired type.

The standard format specification is based on the format specification in Python and has the following syntax:

fill-and-align(optional) sign(optional) #(optional) 0(optional) width(optional) precision(optional) L(optional) type(optional)

These syntax parts are briefly described here.

fill-and-align is an optional fill character, followed by one of the align options:

  • <: Forces the field to be left-aligned with the available space.
  • >: Forces the field to be right-aligned with the available space.
  • ^: Forces the field to be centered with the available space. To do so, it will insert n/2 characters to the left and n/2 characters to the right:
    auto t1 = std::format("{:5}", 42);    // "   42"
    auto t2 = std::format("{:5}", 'x');   // "x    "
    auto t3 = std::format("{:*<5}", 'x'); // "x****"
    auto t4 = std::format("{:*>5}", 'x'); // "****x"
    auto t5 = std::format("{:*^5}", 'x'); // "**x**"
    auto t6 = std::format("{:5}", true);  // "true "
    

sign, #, and 0 are only valid when a number (either an integer or a floating-point) is used. The sign can be one of:

  • +: Specifies that the sign must be used for both negative and positive numbers.
  • -: Specifies that the sign must be used only for negative numbers (which is the implicit behavior).
  • A space: Specifies that the sign must be used for negative numbers and that a leading space must be used for non-negative numbers:
    auto t7 = std::format("{0:},{0:+},{0:-},{0: }", 42);
    // "42,+42,42, 42"
    auto t8 = std::format("{0:},{0:+},{0:-},{0: }", -42);
    // "-42,-42,-42,-42"
    

The symbol # causes the alternate form to be used. This can be one of the following:

  • For integral types, when binary, octal, or hexadecimal representation is specified, the alternate form adds the prefix 0b, 0, or 0x to the output.
  • For floating-point types, the alternate form causes a decimal-point character to always be present in the formatted value, even if no digits follow it. In addition, when g or G are used, the trailing zeros are not removed from the output.

The digit 0 specifies that leading zeros should be outputted to the field width, except when the value of a floating-point type is infinity or NaN. When present alongside an align option, the specifier 0 is ignored:

auto t9  = std::format("{:+05d}", 42); // "+0042"
auto t10 = std::format("{:#05x}", 42); // "0x02a"
auto t11 = std::format("{:<05}", -42); // "-42  "

width specifies the minimum field width and can be either a positive decimal number or a nested replacement field. The precision field indicates the precision for floating-point types or, for string types, how many characters will be used from the string. It is specified with a dot (.), followed by a non-negative decimal number or a nested replacement field.

Locale-specific formatting is specified with the uppercase L and causes the locale-specific form to be used. This option is only available for arithmetic types.

The optional type determines how the data will be presented in the output. The available string presentation types are shown in the following table:

Type

Presentation type

Description

Strings

none, s

Copies the string to the output.

Integral types

B

Binary format with 0b as a prefix.

B

Binary format with 0B as a prefix.

C

Character format. Copies the value to the output as it was a character type.

none or d

Decimal format.

O

Octal format with 0 as a prefix (unless the value is 0).

X

Hexadecimal format with 0x as a prefix.

X

Hexadecimal format with 0X as a prefix.

char and wchar_t

none or c

Copies the character to the output.

b, B, c, d, o, x, X

Integer presentation types.

bool

none or s

Copies true or false as a textual representation (or their local-specific form) to the output.

b, B, c, d, o, x, X

Integer presentation types.

Floating-point

A

Hexadecimal representation. Same as if calling std::to_chars(first, last, value, std::chars_format::hex, precision) or std::to_chars(first, last, value, std::chars_format::hex), depending on whether precision is specified or not.

A

Same as a except that it uses uppercase letters for digits above 9 and uses P to indicate the exponent.

E

Scientific representation. Produces the output as if calling std::to_chars(first, last, value, std::chars_format::scientific, precision).

E

Similar to e except that it uses E to indicate the exponent.

f, F

Fixed representation. Produces the output as if by calling std::to_chars(first, last, value, std::chars_format::fixed, precision). When no precision is specified, the default is 6.

G

General floating-point representation. Produces the output as if by calling std::to_chars(first, last, value, std::chars_format::general, precision). When no precision is specified, the default is 6.

G

Same as g except that it uses E to indicate the exponent.

Pointer

none or p

Pointer representation. Produces the output as if by calling std::to_chars(first, last, reinterpret_cast<std::uintptr_t>(value), 16) with the prefix 0x added to the output. This is available only when std::uintptr_t is defined; otherwise, the output is implementation-defined.

The chrono format specification has the following form:

fill-and-align(optional) width(optional) precision(optional) chrono-spec(optional)

The fill-and-align, width, and precision fields have the same meaning as in the standard format specification, described previously. The precision is only valid for std::chrono::duration types when the representation type is a floating-point type. Using it in other cases throws an std::format_error exception.

The chrono specification can be empty, in which case the argument is formatted as if by streaming it to an std::stringstream and copying the result string. Alternatively, it can consist of a series of conversion specifiers and ordinary characters. Some of these format specifiers are presented in the following table:

Conversion specifier

Description

%%

Writes a literal % character.

%n

Writes a newline character.

%t

Writes a horizontal tab character.

%Y

Writes the year as a decimal number. If the result is less than four digits, it is left-padded with 0 to four digits.

%m

Writes the month as a decimal number (January is 01). If the result is a single digit, it is prefixed with 0.

%d

Writes the day of month as a decimal number. If the result is a single decimal digit, it is prefixed with 0.

%w

Writes the weekday as a decimal number (0-6), where Sunday is 0.

%D

Equivalent to %m/%d/%y.

%F

Equivalent to %Y-%m-%d.

%H

Writes the hour (24-hour clock) as a decimal number. If the result is a single digit, it is prefixed with 0.

%I

Writes the hour (12-hour clock) as a decimal number. If the result is a single digit, it is prefixed with 0.

%M

Writes the minute as a decimal number. If the result is a single digit, it is prefixed with 0.

%S

Writes the second as a decimal number. If the number of seconds is less than 10, the result is prefixed with 0.

%R

Equivalent to %H:%M.

%T

Equivalent to %H:%M:%S.

%X

Writes the locale's time representation.

The complete list of format specifiers for the chrono library can be consulted at https://en.cppreference.com/w/cpp/chrono/system_clock/formatter.

See also

  • Using std::format with user-defined types to learn how to create custom formatting specialization for user-defined types
  • Converting between numeric and string types to learn how to convert between numbers and strings
CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
Modern C++ Programming Cookbook
notes
bookmark Notes and Bookmarks search Search in title playlist Add to playlist download Download options font-size Font size

Change the font size

margin-width Margin width

Change margin width

day-mode Day/Sepia/Night Modes

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY

Submit Your Feedback

Modal Close icon
Modal Close icon
Modal Close icon