Softwareentwicklung: Die Formatierungsbibliothek in C++20
Die Serie zur Formatierungsbibliothek in C++20, dessen Basis das C++20-Buch des Blogautors ist, sollen als Nachschlagewerk dienen.

(Bild: Dirtymono/Shutterstock)
- Rainer Grimm
Obwohl Peter Gottschling zwei großartige Blogartikel über die Formatierungsbibliothek in C++20 geschrieben hat ("std::format in C++20", "C++20: std::format um benutzerdefinierte Datentypen erweitern"), werde ich erneut über die Formatierungsbibliothek schreiben. Der Grund dafür ist einfach: Peters Artikel hat eine tolle Einführung und Übersicht gegeben. Ich möchte alle Details vorstellen, damit alle diesen und die kommenden Artikel als Nachschlagewerk nutzen können.
C++20 unterstĂĽtzt die folgenden Formatierungsfunktionen:
Die Funktionen std::format
und std::format_to
sind funktional äquivalent zu ihren Pendants std::vformat
und std::vformat_to
, aber sie unterscheiden sich in einigen Punkten:
std::format, std::_format_to
undstd::format_to_n
: Sie benötigen zur Compile-Zeit einen Wert als Formatstring. Dieser Formatstring kann einconstexpr
-String oder ein String-Literal sein.std::vformat
undstd::vformat_t:
Der Formatstring kann ein lValue sein. Die Argumente mĂĽssen an die variadische Funktionstd::make_format_args
ĂĽbergeben werden, beispielsweise:std::vformat(formatString, std::make_format_args(args))
.
Die Formatierungsfunktionen akzeptieren eine beliebige Anzahl von Argumenten. Das folgende Programm vermittelt einen ersten Eindruck von den Funktionen std::format
, std::format_to
und std::format_to_n
.
// format.cpp
#include <format>
#include <iostream>
#include <iterator>
#include <string>
int main() {
std::cout << '\n';
std::cout << std::format("Hello, C++{}!\n", "20")
<< '\n'; // (1)
std::string buffer;
std::format_to( // (2)
std::back_inserter(buffer),
"Hello, C++{}!\n",
"20");
std::cout << buffer << '\n';
buffer.clear();
std::format_to_n( // (3)
std::back_inserter(buffer), 5,
"Hello, C++{}!\n",
"20");
std::cout << buffer << '\n';
std::cout << '\n';
}
Das Programm zeigt in (1) direkt die formatierte Zeichenkette an. Die Aufrufe in den (2) und (3) verwenden jedoch einen String als Puffer. AuĂźerdem schiebt std::format_to_n
nur fĂĽnf Zeichen in den Puffer.
Hier ist also das entsprechende Programm, das std::vformat
und std::vformat_n
verwendet:
// formatRuntime.cpp
#include <format>
#include <iostream>
#include <iterator>
#include <string>
int main() {
std::cout << '\n';
std::string formatString = "Hello, C++{}!\n";
std::cout << std::vformat(formatString,
std::make_format_args("20"))
<< '\n'; // (1)
std::string buffer;
std::vformat_to( // (2)
std::back_inserter(buffer),
formatString,
std::make_format_args("20"));
std::cout << buffer << '\n';
}
Der formatString
in den (1) und (2) ist ein lValue.
Vermutlich ist der spannendste Teil der Formatierungsfunktionen der Formatstring ("Hallo, C++{}!\n"
).
Formatstring
Die Syntax des Formatstrings ist bei den Formatierungsfunktionen std::format,
std::format_to, std::format_to_n, std::vformat
und std::vformat_to
identisch. Ich verwende std::format
in meinen Beispielen.
- Syntax: std::format(FormatString, Args)
Der Formatring FormatString
besteht aus
- Gewöhnlichen Zeichen (außer { und }),
- Escape-Sequenzen {{ und }}, die durch { und } ersetzt werden sowie
- Ersetzungsfeldern.
Ein Ersatzfeld hat das Format { }.
- Man kann eine Argument-ID und einen Doppelpunkt innerhalb des Ersetzungsfeldes verwenden, gefolgt von einer Formatangabe. Beide Komponenten sind optional.
Mit der Argument-ID lässt sich der Index der Argumente in Args
angeben. Die IDs beginnen mit 0. Wenn keine Argument-ID angegeben bst, werden die Felder in der gleichen Reihenfolge ausgefĂĽllt, in der die Argumente angegeben werden. Entweder mĂĽssen alle Ersetzungsfelder eine Argument-ID verwenden oder keine; das heiĂźt std::format("{}, {}", "Hallo", "Welt")
und std::format("{1}, {0}", "Welt", "Hallo")
werden beide kompiliert, aber std::format("{1}, {}", "Welt", "Hallo")
nicht.
std::formatter
und seine Spezialisierungen definieren die Formatspezifikation fĂĽr die Argumenttypen.
- Basisdatentypen und
std::string
: basieren auf der Formatspezifikation von Python. - Chrono-Typen: Ich stelle sie in einem der nächsten Artikel vor.
- Andere formatierbare Datentypen: Benutzerdefinierte
std::formatter
-Spezialisierung. Ich werde sie in einem weitern Artikel vorstellen.
Die Tatsache, dass die Formatstrings eine Compile-Zeit-Variable sind, hat zwei interessante Konsequenzen: Performanz und Sicherheit.
Compile-Zeit
- Performanz: Wenn der Formatstring zur Compile-Zeit ĂĽberprĂĽft wird, muss zur Laufzeit nichts getan werden. Folglich versprechen die drei Funktionen
std::format, std::format_to
undstd::format_to_n
eine hervorragende Performanz. Die Prototyp-Bibliothek fmt hat ein paar spannende Benchmarks. - Sicherheit: Die Verwendung eines fehlerhaften Formatstrings zur Compile-Zeit fĂĽhrt zu einem Kompilierungsfehler. Im Gegensatz dazu fĂĽhrt die Verwendung eines Formatstrings zur Laufzeit mit
std::vformat
oderstd::vformat_to
zu einerstd::format_error
-Ausnahme.
Wie geht's weiter?
Im nächsten Blogartikel werde ich die Theorie mit der Praxis ergänzen.
(rme)