Die Bibliothek Boost.Utility ist ein Sammelsurium verschiedener Klassen und Funktionen, die sich als sehr nützlich herausgestellt haben, jedoch zu klein sind, um in eigenen Bibliotheken gepflegt zu werden. Da die von Boost.Utility angebotenen Hilfsmittel so klein sind, lassen sie sich schnell erlernen. Weil Boost.Utility jedoch ein Sammelbecken für alles Mögliche ist, hängen die Hilfsmittel in keiner Weise zusammen.
Wie in diesem Buch üblich soll anhand von Beispielen vorgestellt werden, was die Bibliothek zu bieten hat. Weil sich in Boost.Utility verschiedenste und voneinander unabhängige Hilfsmittel befinden, bauen die folgenden Beispiele jedoch nicht aufeinander auf.
Boost.Utility stellt mit boost/utility.hpp
eine Headerdatei zur Verfügung, über die auf verschiedene Hilfsmittel zugegriffen werden kann. Da jedoch nicht alle Hilfsmittel über boost/utility.hpp
eingebunden werden, wird in den folgenden Beispielprogrammen jeweils auf genau die Headerdatei zugegriffen, in der das jeweilige Hilfsmittel definiert ist.
Mit Boost 1.55.0 wurde eine neue Bibliothek namens Boost.Core eingeführt, in die einige Hilfsmittel aus Boost.Utility übertragen wurden. So befindet sich zum Beispiel boost::checked_delete()
seit dieser Version in Boost.Core. Da es sich lediglich um eine Reorganisation in den Boost-Bibliotheken handelt, haben sich weder die Hilfsmittel an sich noch die Headerdateien, in denen sie sich befinden, geändert. So können Sie die Beispiele in diesem Kapitel auch mit Boost 1.55.0 oder einer neueren Version kompilieren.
boost::checked_delete()
in Aktion#include <boost/checked_delete.hpp>
#include <boost/intrusive/list.hpp>
#include <string>
#include <utility>
#include <iostream>
struct animal : public boost::intrusive::list_base_hook<>
{
std::string name_;
int legs_;
animal(std::string name, int legs) : name_{std::move(name)},
legs_{legs} {}
};
int main()
{
animal *a = new animal{"cat", 4};
typedef boost::intrusive::list<animal> animal_list;
animal_list al;
al.push_back(*a);
al.pop_back_and_dispose(boost::checked_delete<animal>);
std::cout << al.size() << '\n';
}
Im Beispiel 69.1 wird eine Funktion boost::checked_delete()
als Parameter an die Methode pop_back_and_dispose()
übergeben, die die in der Bibliothek Boost.Intrusive definierte Klasse boost::intrusive::list
zur Verfügung stellt. Während boost::intrusive::list
und pop_back_and_dispose()
im Kapitel 18 vorgestellt werden, handelt es sich bei boost::checked_delete()
um eine von Boost.Utility zur Verfügung gestellte Funktion. Um diese Funktion nutzen zu können, muss die Headerdatei boost/checked_delete.hpp
eingebunden werden.
boost::checked_delete()
erwartet als einzigen Parameter einen Zeiger auf ein Objekt, das mit delete
zerstört wird. Da an pop_back_and_dispose()
eine Funktion übergeben werden muss, die einen Zeiger als einzigen Parameter erwartet, um das entsprechende Objekt zu zerstören, bietet es sich an, auf die von Boost.Utility zur Verfügung gestellte Funktion zuzugreifen. So muss die Funktion nicht selbst definiert werden.
Der Unterschied zwischen boost::checked_delete()
und einer selbst definierten Funktion, die lediglich auf delete
zugreift, ist: boost::checked_delete()
stellt sicher, dass der Typ des Objekts, das zerstört wird, vollständig ist. delete
hingegen akzeptiert auch Zeiger auf Objekte, deren Typ nicht vollständig ist. Es handelt sich hierbei um ein Detail aus dem C++-Standard, das Sie ignorieren können, wenn es Ihnen nicht bekannt ist. Der Vollständigkeit halber muss jedoch erwähnt werden, dass boost::checked_delete()
nicht völlig identisch ist mit einem Aufruf von delete
, sondern etwas höhere Anforderungen stellt.
Neben boost::checked_delete()
stellt Boost.Utility boost::checked_array_delete()
zur Verfügung. Diese Funktion muss verwendet werden, wenn ein Array zerstört werden soll. Sie ruft delete[]
statt delete
auf. Darüber hinaus stehen mit boost::checked_deleter
und boost::checked_array_deleter
zwei Klassen zur Verfügung, um Funktionsobjekte zu erstellen, die sich so verhalten wie boost::checked_delete()
und boost::checked_array_delete()
.
BOOST_CURRENT_FUNCTION
in Aktion#include <boost/current_function.hpp>
#include <iostream>
int main()
{
const char *funcname = BOOST_CURRENT_FUNCTION;
std::cout << funcname << '\n';
}
Im Beispiel 69.2 wird ein Makro namens BOOST_CURRENT_FUNCTION
verwendet, mit dem der Name einer Funktion als Zeichenkette erhalten werden kann – und zwar der Funktion, in der sich das Makro befindet. Dazu muss die Headerdatei boost/current_function.hpp
eingebunden werden.
BOOST_CURRENT_FUNCTION
stellt eine plattformunabhängige Möglichkeit dar, den Namen einer Funktion zu erhalten. Seit C++11 können Sie auf das standardisierte Makro __func__
zugreifen. Vor C++11 boten Compiler wie Visual C++ oder GCC das Makro __FUNCTION__
an. Da es sich hierbei um eine Erweiterung dieser Compiler handelte und nicht um ein Makro, das im Standard definiert war, half BOOST_CURRENT_FUNCTION
, plattformunabhängigen Code zu schreiben.
Beispiel 69.2 unter Windows ausgeführt und mit Visual C++ 2013 übersetzt gibt int __cdecl main(void)
aus.
boost::prior()
und boost::next()
in Aktion#include <boost/next_prior.hpp>
#include <array>
#include <algorithm>
#include <iostream>
int main()
{
std::array<char, 4> a{{'a', 'c', 'b', 'd'}};
auto it = std::find(a.begin(), a.end(), 'b');
auto prior = boost::prior(it, 2);
auto next = boost::next(it);
std::cout << *prior << '\n';
std::cout << *it << '\n';
std::cout << *next << '\n';
}
Mit boost::prior()
und boost::next()
stellt Boost.Utility zwei einfache Funktionen zur Verfügung, um einen Iterator relativ zu einem anderen zu erhalten. Während it im Beispiel 69.3 auf den Buchstaben „b“ im Array zeigt, wird mit prior auf „a“ und mit next auf „d“ verwiesen.
Der Unterschied zwischen boost::prior()
und boost::next()
auf der einen und std::advance()
auf der anderen Seite ist, dass die von Boost.Utility angebotenen Funktionen den Iterator, der als Parameter übergeben wird, nicht verändern. Stattdessen geben Sie einen neuen Iterator zurück.
Beide Funktionen boost::prior()
und boost::next()
akzeptieren neben einem Iterator einen zweiten Parameter. Dieser Parameter gibt an, wie viele Schritte der Iterator vor- oder zurückspringen soll. Während boost::prior()
im Beispiel 69.3 den Iterator zwei Schritte nach hinten setzt, führt der Aufruf von boost::next()
dazu, dass der Iterator einen Schritt nach vorne macht.
Beachten Sie, dass Sie bei Angabe der Schritte in jedem Fall eine positive Zahl angeben müssen – auch dann, wenn Sie mit boost::prior()
Schritte nach hinten machen.
Um boost::prior()
und boost::next()
nutzen zu können, müssen Sie die Headerdatei boost/next_prior.hpp
einbinden.
Die beiden Funktionen sind mit C++11 in die Standardbibliothek aufgenommen worden. Sie heißen dort std::prev()
und std::next()
und sind in der Headerdatei iterator
definiert.
boost::noncopyable
in Aktion#include <boost/noncopyable.hpp>
#include <string>
#include <utility>
#include <iostream>
struct animal : boost::noncopyable
{
std::string name;
int legs;
animal(std::string n, int l) : name{std::move(n)}, legs{l} {}
};
void print(const animal &a)
{
std::cout << a.name << '\n';
std::cout << a.legs << '\n';
}
int main()
{
animal a{"cat", 4};
print(a);
}
Boost.Utility bietet eine Klasse boost::noncopyable
an, die verhindert, dass Objekte kopiert werden können. Dazu muss lediglich von boost::noncopyable
abgeleitet werden. Diese Klasse ist in der Headerdatei boost/noncopyable.hpp
definiert.
Der Effekt ist der Gleiche, wie wenn der Copy-Konstruktor und der Zuweisungsoperator als private Methoden definiert werden oder – was seit C++11 möglich ist – mit delete
explizit aus einer Klasse entfernt werden. Wird boost::noncopyable
als Elternklasse verwendet, kann jedoch unter Umständen die Lesbarkeit des Codes verbessert werden, da deutlicher erkennbar ist, dass Objekte einer Klasse nicht kopiert werden dürfen.
Einige Entwickler ziehen boost::noncopyable
vor, andere explizit mit delete
entfernte Methoden. Sie finden zum Beispiel auf Stack Overflow Argumente beider Seiten.
Beispiel 69.4 kann kompiliert und ausgeführt werden. Würde jedoch der Funktionskopf von print()
geändert werden, so dass der Parameter keine Referenz mehr wäre, sondern ein Objekt vom Typ animal
als Kopie erwartet werden würde, würde der Compiler einen Fehler melden.
boost::addressof()
in Aktion#include <boost/utility/addressof.hpp>
#include <string>
#include <iostream>
struct animal
{
std::string name;
int legs;
int operator&() const { return legs; }
};
int main()
{
animal a{"cat", 4};
std::cout << &a << '\n';
std::cout << boost::addressof(a) << '\n';
}
Mit boost::addressof()
stellt Boost.Utility eine Funktion zur Verfügung, um die Adresse eines Objekts zu erhalten – auch dann, wenn wie im Beispiel 69.5 der Operator operator&
überladen wurde.
Um boost::addressof()
einsetzen zu können, müssen Sie die Headerdatei boost/utility/addressof.hpp
einbinden.
Die Funktion ist mit C++11 in die Standardbibliothek aufgenommen wurden und steht als std::addressof()
in der Headerdatei memory
zur Verfügung.
BOOST_BINARY
in Aktion#include <boost/utility/binary.hpp>
#include <iostream>
int main()
{
int i = BOOST_BINARY(1001 0001);
std::cout << i << '\n';
short s = BOOST_BINARY(1000 0000 0000 0000);
std::cout << s << '\n';
}
Boost.Utility bietet mit BOOST_BINARY
ein Makro an, um Zahlen im Binärformat anzugeben. Denn während mit Präfixen wie 0x
und 0
Zahlen im Hexadezimal- und Oktalformat angegeben werden konnten, unterstützte C++ lange kein Binärformat. Das hat sich erst mit C++14 geändert, das das Präfix 0b
kennt.
Beispiel 69.6 gibt 145
und -32768
aus. Die zweite Zahl ist negativ, weil s auf dem 16-Bit-Typ short
basiert und das 16. Bit – das höchstwertige Bit in short
– auf 1 gesetzt wird. Da dieses Bit das Vorzeichen darstellt, wird in s eine Bitfolge gespeichert, die eine negative Zahl darstellt.
BOOST_BINARY
stellt letztendlich nur eine andere Möglichkeit dar, Zahlen anzugeben. Da Zahlen in C++ standardmäßig den Typ int
haben, entspricht auch BOOST_BINARY
einem int
. Möchten Sie beispielsweise eine Zahl vom Typ long
definieren, können Sie auf das Makro BOOST_BINARY_L
zugreifen. Dies entspricht einer Zahl mit dem Suffix L.
Boost.Utility stellt weitere Makros wie BOOST_BINARY_U
zur Verfügung, um eine Variable ohne Vorzeichenbit zu initialisieren. Dieses ist wie alle anderen Makros in der Headerdatei boost/utility/binary.hpp
definiert.
boost::string_ref
in Aktion#include <boost/utility/string_ref.hpp>
#include <iostream>
boost::string_ref start_at_boost(boost::string_ref s)
{
auto idx = s.find("Boost");
return (idx != boost::string_ref::npos) ? s.substr(idx) : "";
}
int main()
{
boost::string_ref s = "The Boost C++ Libraries";
std::cout << start_at_boost(s) << '\n';
}
Beispiel 69.7 stellt die Klasse boost::string_ref
vor. boost::string_ref
ist eine Referenz auf einen String, die lediglich einen lesenden Zugriff erlaubt. Sie ist in gewisser Weise vergleichbar mit const std::string&
. const std::string&
setzt jedoch voraus, dass ein String vom Typ std::string
existiert. boost::string_ref
kann auch ohne std::string
verwendet werden. Der entscheidende Vorteil von boost::string_ref
ist, dass auf Speicherallokationen verzichtet werden kann, wie sie bei einem vielfältigen Einsatz von std::string
nötig wären.
Beispiel 69.7 sucht in einem String nach dem Wort „Boost“. Wird es gefunden, wird ein String beginnend mit diesem Wort ausgegeben. Wird „Boost“ nicht gefunden, wird ein leerer String ausgegeben. Der String s in der Funktion main()
erhält jedoch nicht den Typ std::string
, sondern boost::string_ref
. Es findet daher keine Speicherallokation mit new
statt. Es wird keine Kopie erstellt, wie es std::string
tun würde. Stattdessen verweist s direkt auf die Zeichenkette „The Boost C++ Libraries“.
Der Typ des Rückgabewerts von start_at_boost()
lautet ebenfalls nicht std::string
, sondern boost::string_ref
. Die Funktion gibt keinen neuen String zurück, sondern eine Referenz. Der von start_at_boost()
zurückgegebene Wert ist entweder ein Substring des Parameters, der an die Funktion übergeben wurde, oder eine leere Zeichenkette. Die Funktionsweise von start_at_boost()
setzt voraus, dass der ursprüngliche String mindestens so lange gültig ist wie die Referenzen vom Typ boost::string_ref
, die auf ihn verweisen. Ist dies wie im obigen Beispiel gewährleistet, können Speicherallokationen, wie sie von std::string
typischerweise vorgenommen werden, vermieden werden.
Boost.Utility stellt einige weitere Hilfsmittel zur Verfügung, auf die in diesem Buch nicht näher eingegangen wird. Es handelt sich hierbei vorwiegend um Hilfsmittel, die sich entweder speziell an Entwickler von Boost-Bibliotheken richten oder in der Template-Metaprogrammierung zum Einsatz kommen. Die Dokumentation von Boost.Utility kann Ihnen einen Überblick über diese weiteren Hilfsmittel verschaffen.