Fragen? Antworten! Siehe auch: Alternativlos
Wer sich noch nie angeschaut hat, wie das funktioniert, könnte annehmen, dass das eigentlich ganz einfach sein könnte. Der Compiler könnte einfach Code generieren, der eine Datenstruktur auf dem Stack führt, die man beim Stack Unwinding dann halt rückwärts durchlatscht, bis jemand die Exception fangen will.
Und, so wie ich das gehört habe, lief das auch so unter Windows, und die haben das patentiert, also konnte das niemand anderes auch so machen. Es hatte auch ein Sicherheitsproblem, nämlich das ein Stack Overflow diese Datenstruktur korrumpieren kann, um so Code Execution zu erlangen.
Also haben sich die Unixer ein anderes ABI überlegt. Wenn du Code mit Exceptions hast, dann generiert der Compiler einmal den normalen Funktionscode und danach einen im Code nicht referenzierten Codepfad, der im Exception-Fall die Destruktoren aufruft. Wie findet man den? Über Metadaten in einem ELF-Segment, das man dann halt parsen muss.
Das wird natürlich erst richtig schwierig, wenn man dynamisch linkt, was ich erstmal nicht machen wollte, weil es dann pro Shared Library ein solches ELF-Segment gibt. Und es wird noch schwieriger, weil die Spec auch sprachübergreifende Exceptions vorsieht, wenn man z.B. Java und C++ zusammenlinken würde. Hatte ich nicht vor.
Egal. Jedenfalls habe ich jetzt ein paar Anläufe gemacht, zu verstehen, was da genau vor sich geht, und was ich minimal implementieren muss.
Bisher hab ich immer auf dem Weg aufgegeben, weil man da dutzende von Funktionen vorhalten muss, und ich keine verständliche, minimale Implementation gefunden habe, von der man lernen kann, wie das funktioniert.
Diesmal habe ich mir gedacht, ich nehm einfach testweise mal die Implementation von LLVM, die es inzwischen gibt, und die auch funktioniert.
Ich habe hier also ein minimales Testprogramm gemacht, in dem eine Funktion je nach ifdef eine Exception wirft oder 23 zurückliefert, und einen Aufrufer mit einer kleinen Klasse mit einem Destruktor, damit es die nötigen ELF-Datenstrukturen überhaupt gibt.
Das Binary ohne geworfene Exception ist 17k groß.
Das Binary mit geworfener Exception ist 178k groß.
Zum Vergleich: Das ist ungefähr so groß wie das aktuelle gatling-Binary ohne OpenSSL.
Ich bin gerade ziemlich entsetzt, muss ich euch sagen. Kleine C++-Binaries sind so natürlich nicht drin, außer man verzichtet auf Exception Handling und damit auf die STL.
Wenn dein Exception Handling über 100k Code braucht, ist dein Verfahren zu komplex. Finde ich.
Update: Leser empfehlen mir gerade diesen Cppcon-Vortrag dazu, den ich irgendwie übersehen habe. Ich verfolge Cppcon-Vorträge normalerweise schon ab und zu mal.