C++11 und Exception-Ptr
von Hubert Schmid vom 2011-11-06
Interessant an C++11 ich primär die Erweiterungen der eigentlichen Sprache. Denn diese Erweiterungen geben dem Entwickler ganz neue Möglichkeiten. Die reinen Bibliothekserweiterungen sind zwar ebenfalls sehr nützlich, aber da sie größtenteils aus boost stammen, setze ich viele von ihnen schon längere Zeit ein. Zwischen diesen beiden Reinformen gibt es aber auch Hybridformen. Damit meine ich Erweiterungen, die zwar in Form einer Bibliothek angeboten werden, aber die Funktionen bereitstellen, die ohne Unterstützung des Compilers und der Laufzeitumgebung nicht realisierbar wären. In diese Kategorie fallen auch die Erweiterungen für Exception-Ptr.Die wichtigsten Bestandteile sind der Typ std::exception_ptr sowie die beiden Funktionen std::current_exception und std::rethrow_exception. Vereinfacht ausgedrückt dienen diese Erweiterungen dazu, Exceptions beliebigen Typs in Variablen zu speichern, innerhalb des Programms zu transportieren und an anderer Stelle wieder zu werfen. In anderen Programmiersprachen wie Java ist diese Aufgabe trivial, da alle Exceptions von einer gemeinsamen Basisklasse abgeleitet sind.In C++ war das bisher allerdings nur sehr eingeschränkt möglich, da Objekte beliebiger Typen (ohne gemeinsame Basisklasse) als Ausnahmen geworfen werden können.Ein guter Anwendungsfall für diese Funktionalität ist der Transport von Exceptions über Thread-Grenzen hinweg, wie beispielsweise beim asynchronen Funktionsaufruf. Wie würde man beispielsweise die Funktion std::async implementieren, damit die Exception unbekanntem Typs aus dem asynchronen Thread beim Aufrufer ankommt?auto something_that_throws = []() { throw 42; };
auto future = std::async(something_that_throws);
try {
future.wait();
} catch (int e) {
// handle error
}Mit den Erweiterungen von C++11 ist das nun relativ einfach möglich. Die Function std::current_exception liefert innerhalb eines Catch-Blocks - sowie innerhalb der Funktionen, die aus diesem Catch-Block heraus aufgerufen werden - einen Zeiger auf ein opaques Exception-Objekt, das die gerade gefangene Exception (oder eine Kopie davon) enthält.struct future
{
std::exception_ptr _ex;
void execute()
{
try {
// execute function
} catch (...) {
_ex = std::current_exception();
}
}
// ...
};Wenn man einen Zeiger auf ein solches Exception-Objekt besitzt, so kann man mit der Funktion std::rethrow_exception die referenzierte Exception wieder werfen. Das ist im Wesentlichen bereits alles, was C++11 in diesem Bereich Neues bietet. Denn viel mehr gemeinsame Eigenschaften haben Exceptions in C++ auch nicht. Wünschenswert wäre es aus meiner Sicht noch eine Möglichkeit an das std::type_info-Objekt der Exception heranzukommen. Dabei ist man aber vorläufig auf Compiler-spezifische Erweiterungen angewiesen, wie beispielsweise die Funktion __cxa_exception_type im Falle der GCC.struct future
{
std::exception_ptr _ex;
// ...
void wait()
{
if (_ex != nullptr) {
std::rethrow_exception(_ex);
} // else ...
}
};Ich gehe davon aus, dass die häufigste Anwendung der neuen Funktionalität im Verketten von Exceptions liegt, ähnlich wie das auch Java vorsieht. Ich selbst habe mir die Möglichkeit in der Vergangenheit schon häufig gewünscht - hauptsächlich für Debugging-Zwecke. Bisher habe ich mir dadurch geholfen, dass ich die gefangene Exception in eine Zeichenkette formatiert und an die neue Exception angehängt habe. Ich gehe davon aus, dass ich in neuem Code die Exceptions direkt verketten werde. Das könnte dann wie in folgendem Code aussehen.try {
// ...
} catch (...) {
throw my_own_exception(
"something failed", std::current_exception());
}Die Bibliothek von C++11 sieht diesen Fall vor und bietet genau dafür die Klasse std::nexted_exception an. Ich bin mir aber noch nicht sicher, ob ich diese Klasse verwenden werde, da ich noch keine Erfahrung damit gesammelt habe. Ich bin nur glücklich darüber, dass ich nun überhaupt die Möglichkeit habe, mit Exceptions unbekannten Typs sinnvoll zu umzugehen.