C++ Erbschafts-Steuer

Politisch kann ich mich mit der Idee der Erbschaftssteuer gut identifizieren, vermutlich deshalb weil mich kein Millionenerbe erwartet.

In Sachen C++ sollte jedenfalls jede unnötige Form von Erbschaft rigoros weggesteuert werden. Und im Gegensatz zur Politik mache ich gerne Nägel mit Köpfen und setze diese Ziel einfach um.


Und wieder einmal hat mich der ehrwürdige alte Zen-Meister OpenWatcom auf eine Lebensweisheit aufmerksam gemacht:

code_bloat = exceptions * inheritance

Vorgeschichte: Welche Ausnahmen gibt es überhaupt?

Anfangs dachte ich, dass Exceptions im Java-Style “übersichtlich” wären. Es gibt im GATE Framework daher eine abstrakte Throwable Klasse, von der eine Exception Basisklasse erbt, und dann gibt es im Namensraum exceptions noch für jeden Result-Code eine Template-Variante, wie z.B.:

  • exceptions::NotImplemented
  • exceptions::NullPointer
  • exceptions::Timeout
  • exceptions::OutOfBounds
  • usw.

In Wahrheit sind die exceptions:: Varianten nur mit einem Result-Code konstruierte Exception(results::ErrorCode) Instanzen.

Zwar habe ich schon häufiger die “Endknoten” per throw geworfen, doch in den catch Blöcken reagiere ich in erste Linie auf Instanzen der Exception Basisklasse.

Der Grund ist einfach: In fast allen Fällen ist es egal, ob z.B.: ein Dateizugriff, wegen eines AccessDenied oder NotAvailable Fehlers abbricht. Man loggt den Fehler und beendet die Operation. Es bringt mir also im Normalfall nichts zwischen den Fehlern zu unterscheiden und falls doch, tut das ein einfaches if ebenso.

Die eine Exception, die viele ist…

Ich könnte also gleich die Basisklasse mit dem entsprechenden Fehlercode werfen. Und um das umzusetzen, nutze ich eine Funktion namens

1void raiseException(result_t resultCode, char const* msg = 0, char const* src = 0, int32_t errCode = 0);

Die machte bisher nichts anderes als den resultCode Parameter auszuwerten und daraus ein
throw exceptions::Something(msg, src, errCode); auszulösen.

Und seit neuem macht sie nur noch:
throw Exception(resultCode, msg, src, errCode);

Und das alleine reichte, um die DOS Varianten meiner GATE Tools um 70 bis 80 KByte zu verkleinern. So wurden aus 290 KB wieder 215 KB, ohne dass sich funktional was änderte.

RTTI und Catch-Blöcke

Mit jeder neuen abgeleiteten Klasse einer abstrakten Basis, wird ein unsichtbares Netz von Typenverknüpfungen um Hintergrund gesponnen, das dann zum Einsatz kommt, wenn zur Laufzeit eine Exception geworfen und schrittweise als ihr Basistyp zum Catch-Block überführt werden muss.

Je mehr Klassen es gibt um so größer wird der generierte Hintergrundcode, der dann am Ende vielleicht nie benutzt wird, aber trotzdem vorhanden sein muss.

Jetzt könnte man natürlich hoffen, dass ein guter Optimizer den Großteil davon wegbekommt, doch wie sich zeigt, passiert das nicht (bzw. nicht immer).

Am einfachsten kann man den Müllberg also dadurch klein halten, dass man im Vorfeld gar keinen Müll mehr produziert. Und dieser Ansatz funktioniert offenbar zu meiner großen Freude.

Fazit

Ich habe die früheren exceptions::Something Klassen per #ifdef deaktiviert, denn vielleicht finde ich in Zukunft doch noch Verwendung für sie. Aber das DOS Beispiel hat mich bereits davon überzeugt, dass “eine” gute Exceptionklasse viel Effizienter ist, als 20 individuelle.

Ich belasse daher die vereinfache Variante und freue mich, dass der ohnehin große Exceptionoverhead wieder ein Stück erträglicher geworden ist.