More Details to Formatting User-Defined Types in C++20
Implementing a formatter for a user-defined type having more than one value in C++20 is challenging.
This post is the 5th post in my miniseries about formatting in C++20. Read the previous ones here:
A Formatter for More Values
Point is a class with three members.
// formatPoint.cpp
#include <format>
#include <iostream>
#include <string>
struct Point {
int x{2017};
int y{2020};
int z{2023};
};
template <>
struct std::formatter<Point> : std::formatter<std::string> {
auto format(Point point, format_context& context) const {
return formatter<string>::format(
std::format("({}, {}, {})", point.x, point.y, point.y), context);
}
};
int main() {
std::cout << '\n';
Point point;
std::cout << std::format("{:*<25}", point) << '\n'; // (1)
std::cout << std::format("{:*^25}", point) << '\n'; // (2)
std::cout << std::format("{:*>25}", point) << '\n'; // (3)
std::cout << '\n';
std::cout << std::format("{} {} {}", point.x, point.y, point.z) << '\n'; // (4)
std::cout << std::format("{0:*<10} {0:*^10} {0:*>10}", point.x) << '\n'; // (5)
std::cout << '\n';
}
In this case, I derive from the standard formatter std::formatter<std::string>. A std::string_view is also possible. std::formatter<Point> creates the formatted output by calling format on std::formatter. This function call already gets a formatted string as a value. Consequentially, all format specifiers of std::string are applicable (lines 1 – 3). On the contrary, you can also format each value of Point. This is precisely happening in lines (4) and (5).
Internationalization
The formatting functions std::format*, and std::vformat* have overloads accepting a locale. These overloads allow you to localize your format string.
The following code snippet shows the corresponding overload of std::format:
template< class... Args >
std::string format( const std::locale& loc,
std::format_string<Args...> fmt, Args&&... args );
To use a given locale, specify L before the type specifier in the format string. Now, you apply the locale in each call of std::format or set it globally with std::locale::global.
?
Modernes C++ Mentoring
Be part of my mentoring programs:
Do you want to stay informed: Subscribe.
?
In the following example, I explicitly apply the German locale to each std::format call.
// internationalization.cpp
#include <chrono>
#include <exception>
#include <iostream>
#include <thread>
std::locale createLocale(const std::string& localString) { // (1)
try {
return std::locale{localString};
}
catch (const std::exception& e) {
return std::locale{""};
}
}
int main() {
std::cout << '\n';
using namespace std::literals;
std::locale loc = createLocale("de_DE");
std::cout << "Default locale: " << std::format("{:}", 2023) << '\n';
std::cout << "German locale: " << std::format(loc, "{:L}", 2023) << '\n'; // (2)
std::cout << '\n';
std::cout << "Default locale: " << std::format("{:}", 2023.05) << '\n';
std::cout << "German locale: " << std::format(loc, "{:L}", 2023.05) << '\n'; // (3)
std::cout << '\n';
auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(33ms);
auto end = std::chrono::steady_clock::now();
const auto duration = end - start;
std::cout << "Default locale: " << std::format("{:}", duration) << '\n';
std::cout << "German locale: " << std::format(loc, "{:L}", duration) << '\n'; // (4)
std::cout << '\n';
const auto now = std::chrono::system_clock::now();
std::cout << "Default locale: " << std::format("{}\n", now);
std::cout << "German locale: " << std::format(loc, "{:L}\n", now); // (5)
std::cout << '\n';
}
The function createLocale (line 1) creates the German locale. If this fails, it returns the default locale that uses American formatting. I use the German locale in lines (2), (3), (4), and (5). To see the difference, I also applied the std::format calls immediately afterward. Consequentially, the local-dependent thousand separator is used for the integral value (line 2), and the locale-dependent decimal point and thousand separator for the floating-point value (line 3). Accordingly, the time duration (line 4) and the time point (line 5) use the given German locale.
领英推荐
The following screenshot shows the program’s output.
What’s Next?
std::formatter and its specializations also define the format specification for the chrono types. Before I write about them, I will dive deeper into the chrono extension of C++20.
anks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dr?ge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschl?ger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, and Philipp Lenk.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero
My special thanks to PVS-Studio
My special thanks to Tipi.build?
My special thanks to Take Up Code
My special thanks to SHAVEDYAKS
Seminars
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
Online Seminars (German)
Contact Me