Softwareentwicklung: Die Formatierungsbibliothek in C++20
![Schreibmaschine](https://heise.cloudimg.io/width/700/q75.png-lossy-75.webp-lossy-75.foil1/_www-heise-de_/imgs/18/4/5/2/8/6/7/7/hello_world_dirtymono-92cf51a984a85758.jpeg)
(Bild: Dirtymono/Shutterstock)
Die Serie zur Formatierungsbibliothek in C++20, dessen Basis das C++20-Buch des Blogautors ist, sollen als Nachschlagewerk dienen.
Obwohl Peter Gottschling zwei großartige Blogartikel über die Formatierungsbibliothek in C++20 geschrieben hat ("std::format in C++20 [1]", "C++20: std::format um benutzerdefinierte Datentypen erweitern [2]"), 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.
![](https://heise.cloudimg.io/width/696/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/4/5/2/8/6/7/7/TimelineCpp20CoreLanguage-e7502c4e54c375e9.png)
C++20 unterstützt die folgenden Formatierungsfunktionen:
![](https://heise.cloudimg.io/width/696/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/4/5/2/8/6/7/7/FormattingFunctions-3e8e57c723828396.png)
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.
![](https://heise.cloudimg.io/width/368/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/4/5/2/8/6/7/7/format-7c1ab8b3340d8c82.png)
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.
![](https://heise.cloudimg.io/width/466/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/4/5/2/8/6/7/7/formatRuntime-d7cf1851dd75b13c.png)
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 [3] 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 [4])
URL dieses Artikels:
https://www.heise.de/-9602498
Links in diesem Artikel:
[1] https://www.heise.de/blog/std-format-in-C-20-4919132.html
[2] https://www.heise.de/blog/C-20-std-format-um-benutzterdefinierte-Datentypen-erweitern-4921299.html
[3] https://github.com/fmtlib/fmt
[4] mailto:rme@ix.de
Copyright © 2024 Heise Medien