Die Boost C++ Bibliotheken

Kapitel 7. Boost.Format

Boost.Format bietet einen Ersatz für die Funktion std::printf() an. Diese Funktion stammt aus dem C-Standard und ermöglicht eine formatierte Datenausgabe. Sie ist weder typsicher noch erweiterbar. Mit Boost.Format steht eine typsichere und erweiterbare Alternative zur Verfügung.

Boost.Format bietet eine Klasse boost::format an, die in der Headerdatei boost/format.hpp definiert ist. Dem Konstruktor dieser Klasse wird ähnlich wie std::printf() ein String übergeben, der Sonderzeichen zur Formatierung enthält. Werte, die die Sonderzeichen ersetzen sollen, werden mit dem Operator operator% verknüpft.

Beispiel 7.1. Formatierte Datenausgabe mit boost::format
#include <boost/format.hpp>
#include <iostream>

int main()
{
  std::cout << boost::format{"%1%.%2%.%3%"} % 12 % 5 % 2014 << '\n';
}

Als Platzhalter werden von Boost.Format Zahlen verwendet, die zwischen zwei Prozentzeichen stehen. Mit diesen Zahlen wird auf Werte verwiesen, die mit dem Operator operator% verknüpft werden. So werden im Beispiel 7.1 die Zahlen 12, 5 und 2014 zum Datum 12.5.2014 verknüpft. Wenn wie in den USA der Monat vor dem Tag stehen soll, können anstatt den Zahlen die Platzhalter ausgetauscht werden.

Beispiel 7.2. Nummerierte Platzhalter mit boost::format
#include <boost/format.hpp>
#include <iostream>

int main()
{
  std::cout << boost::format{"%2%/%1%/%3%"} % 12 % 5 % 2014 << '\n';
}

Beispiel 7.2 gibt als Ergebnis 5/12/2014 aus.

Um Daten zu formatieren, können mit Hilfe der Funktion boost::io::group(), die ebenfalls von Boost.Format zur Verfügung gestellt wird, die aus dem Standard bekannten Manipulatoren angewandt werden.

Beispiel 7.3. Manipulatoren mit boost::io::group() verwenden
#include <boost/format.hpp>
#include <iostream>

int main()
{
  std::cout << boost::format{"%1% %2% %1%"} %
    boost::io::group(std::showpos, 1) % 2 << '\n';
}

Beispiel 7.3 gibt als Ergebnis +1 2 +1 aus. Weil der Manipulator std::showpos() mit boost::io::group() auf die Zahl 1 angewandt wird, wird das Pluszeichen immer dann mitangegeben, wenn 1 ausgegeben werden soll.

Soll nur die erste Ausgabe von 1 ein Pluszeichen mitanzeigen, muss das Sonderzeichen zur Formatierung angepasst werden.

Beispiel 7.4. Platzhalter mit Sonderzeichen
#include <boost/format.hpp>
#include <iostream>

int main()
{
  std::cout << boost::format{"%|1$+| %2% %1%"} % 1 % 2 << '\n';
}

Der Platzhalter %1% wird im Beispiel 7.4 durch %|1$+| ersetzt. Wenn die Formatierung angepasst werden soll, erfordert dies nicht nur zwei zusätzliche vertikale Balken. Der Verweis auf eine Variable muss ebenfalls zwischen die vertikalen Balken gesetzt werden und erfolgt nicht mehr mit 1%, sondern mit 1$. Die Änderungen sind notwendig, um das Pluszeichen unterzubringen, damit als Ergebnis +1 2 1 erhalten wird.

Beachten Sie, dass Verweise auf Variablen optional sind. Sie müssen sich jedoch für alle Platzhalter entscheiden, ob Sie Verweise angeben wollen oder nicht. So ist im Beispiel 7.5 für einen von drei Platzhaltern kein Verweis angegeben, was bei der Ausführung zu einem Fehler führt.

Beispiel 7.5. boost::io::format_error im Fehlerfall
#include <boost/format.hpp>
#include <iostream>

int main()
{
  try
  {
    std::cout << boost::format{"%|+| %2% %1%"} % 1 % 2 << '\n';
  }
  catch (boost::io::format_error &ex)
  {
    std::cout << ex.what() << '\n';
  }
}

Im Beispiel 7.5 wird eine Ausnahme vom Typ boost::io::format_error geworfen. Genaugenommen hat die Ausnahme den Typ boost::io::bad_format_string. Da die verschiedenen Ausnahmen von boost::io::format_error abgeleitet sind, ist es einfacher, Ausnahmen von diesem Typ abzufangen.

Beispiel 7.6 zeigt, wie das Programm ohne Verweise auf Variablen geschrieben werden muss.

Beispiel 7.6. Platzhalter ohne Nummerierung
#include <boost/format.hpp>
#include <iostream>

int main()
{
  std::cout << boost::format{"%|+| %|| %||"} % 1 % 2 % 1 << '\n';
}

Wenn Sie auf die Angabe der vertikalen Balken, die in diesem Fall für den zweiten und dritten Platzhalter die Formatierung nicht näher spezifizieren, verzichten möchten, können Sie dies tun. Sie erhalten dann eine Syntax, die der von std::printf() sehr ähnlich ist.

Beispiel 7.7. boost::format mit der von std::printf() gewohnten Syntax
#include <boost/format.hpp>
#include <iostream>

int main()
{
  std::cout << boost::format{"%+d %d %d"} % 1 % 2 % 1 << '\n';
}

Beachten Sie, dass die Formatierung der von std::printf() ähnlich sieht, Boost.Format jedoch den Vorteil der Typsicherheit bietet. So bedeutet der Buchstabe d im Beispiel 7.7 nicht, dass eine Dezimalzahl ausgegeben werden muss, sondern dass der Manipulator std::dec() auf den Stream angewandt wird, den boost::format intern verwendet. Auf diese Weise können Formatierungen angegeben werden, die für std::printf() ungültig wären und zu einem Laufzeitfehler führen würden.

Beispiel 7.8. boost::format mit vermeintlich ungültigen Platzhaltern
#include <boost/format.hpp>
#include <iostream>

int main()
{
  std::cout << boost::format{"%+s %s %s"} % 1 % 2 % 1 << '\n';
}

Während für std::printf() der Buchstabe s lediglich für Strings verwendet werden darf – also den Typ const char* in C – funktioniert Beispiel 7.8 einwandfrei. Boost.Format erwartet nicht zwingend Strings, sondern setzt lediglich entsprechende Manipulatoren ein, um den internen Stream zu konfigurieren.

Boost.Format ist nicht nur typsicher, sondern auch erweiterbar. So können Sie Objekte beliebiger Typen mit Boost.Format verwenden, solange der Operator operator<< für std::ostream überladen ist.

Beispiel 7.9. boost::format mit benutzerdefiniertem Typ
#include <boost/format.hpp>
#include <string>
#include <iostream>

struct animal
{
  std::string name;
  int legs;
};

std::ostream &operator<<(std::ostream &os, const animal &a)
{
  return os << a.name << ',' << a.legs;
}

int main()
{
  animal a{"cat", 4};
  std::cout << boost::format{"%1%"} % a << '\n';
}

Beispiel 7.9 gibt ein Objekt von einem benutzerdefinierten Typ animal über boost::format auf die Standardausgabe aus. Dies ist möglich, weil der Stream-Operator für animal überladen ist.