Last Updated: August 22, 2023
Originally Published: May 19, 2023
Skill Level: Intermediate
Table Of Contents
- Introduction
- What Is Needed
- Background Information
- Formatted Printing With Standard C
- Formatted Printing With Standard C++
- Formatted Printing With C++20 <format> Library
- Formatted Printing With C++23 <print> Library
- Additional Resources
- Summary
Introduction
This tutorial will show you how to use the new string formatting and printing capabilities available in the new C++20 and C++23 language specifications.
A basic understanding of C/C++ programming is expected.
The resources created for this tutorial are available on GitHub for your reference.
This tutorial is provided as a free service to our valued readers. Please help us continue this endeavor by considering a GitHub sponsorship or a PayPal donation.
What Is Needed
- Personal Computer Or Workstation
- C++ Compiler
Background Information
The C++20 specification defines a new text formatting library, named <format>
, that “offers a safe and extensible alternative to the printf family of functions”. It includes a new std::format()
function that formats text in a similar fashion to the C based printf()
function and is based on Python’s string formatting specification.
The C++23 specification extends the text formatting work in C++20 to enable the printing of formatted text directly to the screen with the std::print()
and std::println()
functions contained within the new <print>
library.
Most of the major compiler vendors have already been incorporating C++20 features into their compilers. It is my understanding that the <format>
library in general, and the std::format()
function in particular, is already available in the MSVC 16.10 (with the /std:c++20 switch), Clang 14 (with the -std=c++20 option), and GCC 13.1 (with the -std=c++20 option) compilers.
Since I am on a Mac, Apple’s version of Clang does not yet support the std::format()
function at this time. However, the latest major GNU Compiler Collection (GCC) version, 13.1, was released on April 26th, 2023 and does provide support for the <format>
library’s std::format()
function. I installed it via Homebrew and that is the compiler I will be using on my system.
If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.
Formatted Printing With Standard C
Before we begin, since we will be attempting to use fairly new features of recent C++ specifications within this tutorial, it is recommended that you install the latest version(s) of your favorite C++ compiler(s).
Let’s start with a simple C++ program that uses the classic C style formatted printing with the printf()
function. Since C++ is a superset of the C language, we still have access to that function in our program.
Create a new project directory, named formatted_output, and then go into that directory. Open your favorite code editor and create the formatted_output.h header and formatted_output.cpp implementation files with the contents shown below.
formatted_output.h (header file):
#ifndef FORMATTED_OUTPUT_H #define FORMATTED_OUTPUT_H #include <climits> #include <cstdio> #include <string> #include <string_view> #ifndef M_PI #define M_PI 3.14159265358979323846 #endif void c_standard(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value); #endif
formatted_output.cpp (implementation file):
#include "formatted_output.h" int main() { constexpr std::string_view world = "World"; constexpr std::string_view int_max_name = "INT_MAX"; constexpr int int_max_value = INT_MAX; constexpr std::string_view pi_name = "Pi"; constexpr float pi_value = M_PI; c_standard(world, int_max_name, int_max_value, pi_name, pi_value); } void c_standard(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value) { printf("C standard formatted printing with printf():\n"); printf("Hello, %s!\n", std::string(string_value).c_str()); printf("%7s = %10d\n", std::string(int_name).c_str(), int_value); printf("%7s = %10.5f\n\n", std::string(float_name).c_str(), float_value); }
The header file begins by including the libraries we need for the program. <climits>
includes the INT_MAX
macro, <cstdio>
includes the printf()
function, and <string>
and <string_view>
include the std::string
and std::string_view
datatypes respectively.
Some, but not all, compilers define the M_PI
macro as the value of the Pi mathematical constant. Lines 9-11 just define it if it is not already defined.
Lines 13-15 declare our c_standard()
function prototype that will be defined in the implementation file.
The main()
function in the implementation file begins by defining various example variables (lines 4-8) that we will be using throughout our program. Those variables are then passed into the c_standard()
demonstration function that will utilize the C based printf()
function to format and print those variables to the screen.
I am sure most of you are familiar with the printf()
function notation, but let me offer a quick synopsis of what each line does.
Line 16 prints a simple string to the screen. I am also using this line to communicate the formatted printing method that is being utilized by this particular demonstration function.
Line 17 prints a string using simple string substitution. However, since the printf()
function expects the classic C-string datatype, we need to convert the std::string_view
type to a C-string before it can be used by the printf()
function. To do this, we first cast the std::string_view
type to a std::string
type and then apply the std::string
‘s c_str()
method to make the conversion.
Line 18 prints a compound string using string and integer substitution and formatting. The %7s
specifier specifies a string field with a width of 7 characters and %10d
specifies an integer field with a width of 10.
Line 19 prints a compound string using string and floating point substitution and formatting. %10.5f
specifies a floating point field with a width of 10 using 5 decimal places of precision. I also added an additional blank line (\n
) to the end of this statement.
For more information about the printf()
function’s formatting specifications, see the printf()
function’s documentation.
Since we will cover multiple demonstration examples of formatted printing functionality in the following sections, I separated them into their own distinct functions. Therefore, the c_standard()
function, this first example, is the only function I am currently calling within the main()
function.
If there is something that needs further explanation, please let me know in the comment section and I will try to answer your question.
Save your code when you are done editing and then compile the program with your favorite C++ compiler. I used the following to compile my program with GCC on the command line. The -Wall and -Wextra options were used to report additional warnings of potential code issues found by the compiler.
$ g++-13 -Wall -Wextra -o formatted_output formatted_output.cpp
Next, run the program
$ ./formatted_output
and you should see the following printed to your screen. Note, you may see a different value for INT_MAX as it depends on both your compiler and the system architecture being used.
C standard formatted printing with printf(): Hello, World! INT_MAX = 2147483647 Pi = 3.14159
Formatted Printing With Standard C++
While many people like to use the C based printf()
function in their C++ code, many others argue that it is unsafe to do so. The use of std::cout
, std::endl
, and other stream based objects and functions are the recommended method for printing to the screen in C++. This section demonstrates how to use that preferred method to print the same formatted output we printed in the previous section that used printf()
.
Update the formatted_output.h header file to add the extra libraries
#include <iomanip> #include <iostream>
and the cpp_standard()
function prototype.
void cpp_standard(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value);
The <iostream>
library adds the std::cout
, std::endl
, and std::fixed
stream objects while the <iomanip>
library adds the std::setprecision()
and std::setw()
functions that will be used in the cpp_standard()
example function.
Update the formatted_output.cpp implementation file to add the cpp_standard()
function
void cpp_standard(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value) { std::cout << "C++ standard printing with std::cout and std::endl and formatting with std::fixed, std::setprecision(), and std::setw():\n"; std::cout << "Hello, " << string_value << "!\n"; std::cout << std::setw(7) << int_name << " = " << std::setw(10) << int_value << "\n"; std::cout << std::fixed << std::setprecision(5); std::cout << std::setw(7) << float_name << " = " << std::setw(10) << float_value << "\n" << std::endl; }
and then add it to the main()
function after the c_standard()
function call.
cpp_standard(world, int_max_name, int_max_value, pi_name, pi_value);
Within the cpp_standard()
example function, the std::setw()
function is added to the output stream whenever we want to define the character width to use for the next item in the stream. The std::fixed
object and std::precision()
function are used to fix the number of decimal digits used to represent a floating point number.
Save, compile, and run the updated program and you should see the following output.
C standard formatted printing with printf(): Hello, World! INT_MAX = 2147483647 Pi = 3.14159 C++ standard printing with std::cout and std::endl and formatting with std::fixed, std::setprecision(), and std::setw(): Hello, World! INT_MAX = 2147483647 Pi = 3.14159
The three formatted lines after the method descriptions should be identical between the C standard and C++ standard sections.
When comparing the cpp_standard()
function against the c_standard()
function, it becomes apparent that it takes quite a bit more code to accomplish the same string formatting in “standard” C++ as it does in “standard” C. In addition, some folks also find it even more cumbersome to use. These are just some of the reasons why they continue using the old-time classic printf()
function.
Formatted Printing With C++20 <format> Library
The designers of the C++20 specification tried to alleviate those concerns mentioned previously by implementing a new text formatting library, named <format>
. It contains the new std::format()
function that uses string interpolation that is not only similar to C’s printf()
function but also to the text formatting methods implemented by other programming languages as well.
Update the header file to include the <format>
library by adding the following just below the existing #include
statements.
#ifdef __has_include #if __has_include(<format>) #include <format> #else #warning "The <format> library is not available." #endif #else #warning "The __has_include check is not available." #endif
The __has_include
macro (added in C++17) checks if a library or other source file is available. If it is, we include it. Here, we are checking the availability of the <format>
library before including it in our program. I also implemented compiler warnings to let the user know if the __has_include
macro or <format>
library is not available for use. Note, the #warning
directive has been implemented in various compilers for quite some time, but was not officially part of the standard specification until C++23.
You will also need to add the following cpp_format()
function prototype to the header file.
void cpp_format(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value);
Update the implementation file to add the cpp_format()
example function
void cpp_format(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value) { #ifdef __cpp_lib_format std::cout << "C++ standard printing with std::cout and std::endl and <format> based formatting with std::format():\n"; std::cout << std::format("Hello, {}!\n", string_value); std::cout << std::format("{0:>7s} = {1:10d}\n", int_name, int_value); std::cout << std::format("{0:>7s} = {1:10.5f}\n", float_name, float_value) << std::endl; #else std::cout << "C++ formatting with <format> based std::format() is not yet available; expected in C++20.\n" << std::endl; #endif }
and then add a call to it at the end of the main()
function.
The __cpp_lib_format
feature test macro will only be defined if the <format>
library was included and its features are available for use. Here, we are checking if the macro is defined, if it is, we utilize the std::format()
function, if it is not, we alert the user to the function not yet being available.
While the specifier syntax of the std::format()
function may look different than the syntax used by the printf()
function, the formatting options are actually quite similar. Instead of using the %
placeholder notation, we are using {}
instead. If the {}
placeholder is empty, as in line 6, basic substitution with implicit positional arguments is used. That means the placeholders in the formatted string are replaced with the std::format()
function’s arguments, after the format string, in the same order as they are listed.
If the {}
placeholder contains a colon (:
), the number to the left of the colon refers to the explicit positional argument (which may be empty to use implicit positional arguments), and the characters to the right of the colon specify the formatting of the text to use for the placeholder’s field. In line 8 for instance, the {0:>7s}
placeholder specifies to use the 1st argument, beginning with 0, right justify the field, use a field width of 7 characters, and the field’s type is a string. The {1:10.5f}
placeholder specifies to use the 2nd argument, use a field width of 10 characters, use 5 decimal digits of precision, and the field’s type is a floating point number. Note, specifying the justification was not required when printing strings with printf()
as the default is already right justified.
For more information about the std::format()
function’s capabilities, see the function’s documentation.
Save your program files when you are done updating them.
It is likely that your compiler conforms to a different, earlier, specification of the C++ language by default. For instance, as of GCC 13.1, the default specification is C++17. In order to use the language and library features of a newer specification, we need to tell the compiler which one to use. The MSVC compiler requires the /std:c++20 switch while the Clang and GCC compilers use the -std=c++20 option. I will be using the following to run my updated program with GCC on my Mac.
$ g++-13 -Wall -Wextra -std=c++20 -o formatted_output formatted_output.cpp
Compile and run the program and you should see the same formatted output for this example function as we have seen in the previous sections.
Although usage of the <format>
library is still experimental at this time, at least in GCC’s case, the basics all seem to be in place. Keep in mind that some modifications may be made in the future.
Formatted Printing With C++23 <print> Library
With the above std::format()
function, we now have a more concise syntax to print our formatted text to the screen, but we still have to use the std::cout
stream object to print the formatted text to the screen. The C++23 specification resolves this last “issue” by adding the <print>
library that contains the std::print()
and std::println()
functions. These new functions allow you to print formatted text directly to the screen with a single function call as we were able to do with the classic C printf()
function.
Update the header file to add the <print>
library by replacing the conditional include section with the following that adds the new library.
#ifdef __has_include #if __has_include(<format>) #include <format> #else #warning "The <format> library is not available." #endif #if __has_include(<print>) #include <print> #else #warning "The <print> library is not available." #endif #else #warning "The __has_include check is not available." #endif
Then add the new cpp_print()
function prototype to the header file.
void cpp_print(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value);
Update the implementation file to add the cpp_print()
example function
void cpp_print(std::string_view string_value, std::string_view int_name, int int_value, std::string_view float_name, float float_value) { #ifdef __cpp_lib_print std::println("C++ <print> based formatted printing with std::print() and std::println():"); std::print("Hello, {}!\n", string_value); std::println("{0:>7s} = {1:10d}", int_name, int_value); std::println("{0:>7s} = {1:10.5f}\n", float_name, float_value); #else std::cout << "C++ formatted printing with <print> based std::print() and std::println() is not yet available; expected in C++23.\n" << std::endl; #endif }
and then add its call to the main()
function.
As you can probably see, the std::println()
function replaces the usage of both the std::cout
and std::endl
stream objects along with the std::format()
function while still using the same formatting syntax. The std::println()
function is equivalent to the std::print()
function terminated by an additional new line (\n
). I am not positive that the new std::println()
function will also flush the output stream, as std::endl
does, but I assume that will be the case.
We are also using the new __cpp_lib_print
feature test macro in this cpp_print()
demonstration function to test for inclusion of the <print>
library.
Save and compile the updated program, but this time, specify the C++23 specification. You will probably see warnings about the <print>
library not being available and unused parameters within the cpp_print()
function. That is expected as the <print>
library is probably not implemented by your compiler just yet. If you didn’t get any warnings, that is great news as it means the library is already implemented in your compiler. It appears that MSVC is the only major compiler to have implemented the <print>
library at this time. Other compilers will add it in the future.
Run the program one last time and you should see the output for all of the demonstration example functions. Since I do not yet have access to the <print>
library on my Mac, I get a notification that the library is not yet available.
C standard formatted printing with printf(): Hello, World! INT_MAX = 2147483647 Pi = 3.14159 C++ standard printing with std::cout and std::endl and formatting with std::fixed, std::setprecision(), and std::setw(): Hello, World! INT_MAX = 2147483647 Pi = 3.14159 C++ standard printing with std::cout and std::endl and <format> based formatting with std::format(): Hello, World! INT_MAX = 2147483647 Pi = 3.14159 C++ formatted printing with <print> based std::print() and std::println() is not yet available; expected in C++23.
For the typical programmer, the usage of the std::print()
and std::println()
functions are extremely similar in size and syntax to the classic C based printf()
function and will be a welcomed addition to the language. This should make many folks happy and hopefully end the printf()
usage in C++ programs argument.
Additional Resources
- {fmt} Third Party Open Source Library
Summary
In this tutorial, we learned how to use the new text formatting and printing capabilities of the new C++20 and C++23 specifications.
Specifically, we learned how to print formatted text with strings, integers, and floating point numbers using
- the
printf()
function in standard C, - the
std::cout
andstd::endl
stream objects for printing and thestd::fixed
stream object along with thestd::setprecision()
andstd::setw()
stream functions for formatting in standard C++, - the
std::cout
andstd::endl
stream objects for printing and thestd::format()
function for formatting in C++20, and - the
std::print()
andstd::println()
functions for both printing and formatting in C++23.
Hopefully, this tutorial provided you with a good history of how formatted printing is currently performed in standard C and C++ along with the capabilities we have in the future with the new C++20 and C++23 specifications. Happy programming!
The final program used for this tutorial is available on GitHub. The GitHub version of the code is fully commented to include additional information, such as the program’s description, library references, function parameters, code clarifications, and other details. The comments are also Doxygen compatible in case you want to generate the code documentation yourself. There is also a GCC based Makefile included that you can use to compile and run your program along with generating the documentation if you so choose.
As a reminder, this tutorial was provided as a free service to our valued readers. Please help us continue this endeavor by considering a GitHub sponsorship or a PayPal donation.
Thank you for joining me on this journey and I hope you enjoyed the experience. Please feel free to share your thoughts or questions in the comments section below.
Hi
For functions
void c_standard
void cpp_standard
why aren’t the std::string arguments passed by const reference and not by value?
Thanks
That is a great question.
Since the strings a small, I chose the simplest method of passing in the strings. However, in retrospect, passing in the strings by constant reference probably would have been the preferred choice.
Since we are talking about modern C++ standards, I decided to take it a step further and update the tutorial to utilize the
string_view
class introduced in C++17. Hopefully, the code is now more in line with best practices.