Exceptions in C++ hui oder pfui?
-
cfoobar schrieb:
Wo ich hier den Thread gerade über die Java-Exception-Frage gelesen habe, kam es mir so vor als hätte ich mal was über Exception-Handling in C++ gelesen, so unter dem Motte: "Wenn es geht, nicht mit Exceptions in C++ arbeiten". Habe ich mich da total geirrt oder kann mir das Jemand hier bestätigen und vielleicht auch erklären?
Mag sein dass du das gelesen hast. Vermutlich von jemandem, der Exceptions aus Java kennt und mit C++-Exceptins nichts anzufangen weiß. Die Aussage an sich ist allerdings für mich nicht nachvolziehbar.
Also ganz einfache Frage:"Execptions in C++ hui oder pfui?"
Exceptions sind das Mittel, in C++ schwerwiegende Fehler zu behandeln. Allerdings muss man dazu natürlich exceptionsicheren Code schreiben (können) und wissen, an welcher Stelle Exceptions angebracht sind und wo nicht. Was auf jeden Fall pfui ist, sind Exception-Spezifikationen. Die bringen nicht das, was man sich naiv von ihnen erhofft, einige Compiler ignorieren sie einfach, bei anderen sind sie schrecklich imperformant usw. - das ist einer der Gründe, warum sie im aktuellen Standard als veraltet gelten, mit Ausnahme der noexcept-Spezifikation.
Das Wichtigste zum Umgang mit Exceptions ist allerdings die Aussage, dass exceptionsicherer Code nichts mit andauerndem try/catch zu tun hat. Leider verstehen das viele Leute immernoch nicht.
-
cfoobar schrieb:
Guten Abend,
Wo ich hier den Thread gerade über die Java-Exception-Frage gelesen habe, kam es mir so vor als hätte ich mal was über Exception-Handling in C++ gelesen, so unter dem Motte: "Wenn es geht, nicht mit Exceptions in C++ arbeiten". Habe ich mich da total geirrt oder kann mir das Jemand hier bestätigen und vielleicht auch erklären?
Das haste falsch verstanden.
Wo zur Laufzeit was unvorhersagbar ist, wie, ob man genug Speicher hat oder ob sich die Datei öffnen läßt, sind Exceptions voll ok. Erst mit Exceptions und RAII wird C++ stark. Sonst wäre es nur C mit unwichtigem Syntaxzucker.Was aber auch gilt: In C++ werfen wir tunlichst keine Exceptions bei Programmierfehlern (Stack Underflow, Arraygrenzenüberschreitung und so) und auch nicht bei ganz normalen Sachen wie einem Dateiende.
-
[quote="volkard"]
cfoobar schrieb:
Erst mit Exceptions und RAII wird C++ stark.
...
Was aber auch gilt: In C++ werfen wir tunlichst keine Exceptions bei Programmierfehlern (Stack Underflow, Arraygrenzenüberschreitung und so) und auch nicht bei ganz normalen Sachen wie einem Dateiende.Also ist C++ selten stark.
-
Das sogar die std::fstreams, selbst bei Dateizugriffsfehlern, standardmäsig keine Exceptions werfen, sollte einem doch zu denken geben.
-
http_//www.c-plusplus.net/ schrieb:
volkard schrieb:
Erst mit Exceptions und RAII wird C++ stark.
...
Was aber auch gilt: In C++ werfen wir tunlichst keine Exceptions bei Programmierfehlern (Stack Underflow, Arraygrenzenüberschreitung und so) und auch nicht bei ganz normalen Sachen wie einem Dateiende.Also ist C++ selten stark.
Völlig unlogisches Getrolle.
Und schon im Voraus widerlegt
pumuckl schrieb:
Das Wichtigste zum Umgang mit Exceptions ist allerdings die Aussage, dass exceptionsicherer Code nichts mit andauerndem try/catch zu tun hat. Leider verstehen das viele Leute immernoch nicht.
-
Wenn Ihr mehr Fragen zu C++ habt, im C++-Forum geht das besser.
-
volkard schrieb:
Wenn Ihr mehr Fragen zu C++ habt, im C++-Forum geht das besser.
Wissen wir. Da kannst du nämlich die unangenehme Wahrheit, die dir nicht gefällt, einfach löschen.
volkard schrieb:
"Zensur ist immer etwas Schlechtes."
-
volkard schrieb:
http_//www.c-plusplus.net/ schrieb:
volkard schrieb:
Erst mit Exceptions und RAII wird C++ stark.
...
Was aber auch gilt: In C++ werfen wir tunlichst keine Exceptions bei Programmierfehlern (Stack Underflow, Arraygrenzenüberschreitung und so) und auch nicht bei ganz normalen Sachen wie einem Dateiende.Also ist C++ selten stark.
Völlig unlogisches Getrolle.
Und schon im Voraus widerlegt
pumuckl schrieb:
Das Wichtigste zum Umgang mit Exceptions ist allerdings die Aussage, dass exceptionsicherer Code nichts mit andauerndem try/catch zu tun hat. Leider verstehen das viele Leute immernoch nicht.
Genau, ich halte try/catch für exceptionsicheren Code.
Du hast meine Aussage überhaupt nicht verstanden.
-
Verlinkt doch mal ein Open Source C++ Projekt, dass eurer Meinung nach Exceptions effektiv verwendet.
-
volkard schrieb:
cfoobar schrieb:
Guten Abend,
Wo ich hier den Thread gerade über die Java-Exception-Frage gelesen habe, kam es mir so vor als hätte ich mal was über Exception-Handling in C++ gelesen, so unter dem Motte: "Wenn es geht, nicht mit Exceptions in C++ arbeiten". Habe ich mich da total geirrt oder kann mir das Jemand hier bestätigen und vielleicht auch erklären?
Das haste falsch verstanden.
Wo zur Laufzeit was unvorhersagbar ist, wie, ob man genug Speicher hat oder ob sich die Datei öffnen läßt, sind Exceptions voll ok. Erst mit Exceptions und RAII wird C++ stark. Sonst wäre es nur C mit unwichtigem Syntaxzucker.Was aber auch gilt: In C++ werfen wir tunlichst keine Exceptions bei Programmierfehlern (Stack Underflow, Arraygrenzenüberschreitung und so) und auch nicht bei ganz normalen Sachen wie einem Dateiende.
Danke, ich halte das hier mal für den besten Tipp mit Exceptions umzugehen. Auf RAII achte ich sowieso, da ich damit den Kopf ein wenig freier habe^^
Was für Fehlerbandlungsdesigns habt ihr sonst so im Einsatz? Mir fällt noch das Setzen eines Error-Boolean in der Klasse mit einem Message-Token ein, welches dann je nach Sprache den Text einsetzt.
Was hat sich bei euch bewährt?
-
cfoobar schrieb:
Was für Fehlerbandlungsdesigns habt ihr sonst so im Einsatz? Mir fällt noch das Setzen eines Error-Boolean in der Klasse mit einem Message-Token ein, welches dann je nach Sprache den Text einsetzt.
Was hat sich bei euch bewährt?
In Wirklichkeit mag ich eine AssertException, die Dateiname und Zeilennummer trägt und von der main() gefangen ausgegeben wird (und zwar Nur in der main()!, damit werden Datenbanken etc geschlossen trotz "Absturz"), aber beim Werfen wird mit __asm int 3 in den Debugger gesprungen, falls er an ist. In der Ausgestaltung hat man ja eine breite Wahl. Viele Leute möchten umfangreich Loggen. Ist nicht mein Ding. Bei mir bleibt's im Prinzip bei Exceptions für ungewöhnliche Laufzeitfehler und assert für Programmierfehler. Wer über's Dateiende hinausliest oder die Wurzel aus -1 berechnet oder einen leeren Stack popt, der hat halt Pech gehabt. Man kann nach Belieben so viel assert reinsteuen, wie man will.
-
Dieser Thread wurde von Moderator/in rüdiger aus dem Forum Rund um die Programmierung in das Forum C++ (auch C++0x, bzw. C++11) verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Ich finde man sollte Exceptions sehr sparsam einsetzen und nicht wie in Java üblich damit um sich werfen. Ich bin bekanntermassen ein gebranntes Kind was das Java-Verständnis von Exception-Safety angeht. Programmierfehler sollten keine Exceptions werfen; Programmierfehler sollten zwingend dazu führen, dass das ganze Programm bedingungslos abschmiert. Alles andere führt nur dazu, dass fehlerhafte Programme weiterleben.
Die Funktionen sollten in der Regel nur dann eine Exception werfen, wenn sie ohne offensichtliches Verschulden des Programmierers nicht in der Lage sind, ihre versprochene Funktionalität umzusetzen. Für alles andere gibt es Assertions, welche Testen, ob der Programmierer selbst seine eigenen Versprechen einhält.
Wenn der Programmierer einen ungültigen Index für den Zugriff in meine Collection verwendet, ist das nicht mein Problem. Wenn er einen Null-Zeiger an meine Funktion übergibt, ist das nicht mein Problem. Wenn er allfällige Exceptions, die geworfen werden könnten, nicht abfangen will, dann ist es nicht mein Problem. Er wird seine Gründe dafür haben.
-
Wir sind uns einig, dass Ausnahmen für Ausnahmefälle da sind, aber was sind denn in der Praxis solche Fälle?
Ist es wirklich so unerwartet, dass eine Datei nicht geöffnet werden kann?
Ist es eine Ausnahme, wenn eine TCP-Verbindung geschlossen wird oder verloren geht?
Sollte man auf einen Mangel an Speicher reagieren oder ist es dann nicht schon zu spät?
Ist es nicht eher ein Hack, wenn man Fehler im Konstruktor per Ausnahme meldet, obwohl man bei Funktionen mit Rückgabewert diesen benutzt?
Sollte manSystem.Exceptionoderstd::exceptionfangen und damit möglicherweise Programmierfehler verschleiern?Ich weiß noch keine sinnvollen Antworten auf diese Fragen.
Dass man mitassertnicht sparen sollte, ist aber klar. Und wenn man Referenzen verwendet, wo Referenzen angebracht sind, muss man sogar nicht einmal auf Nullzeiger testen.
-
-
Also, ich sehe das so:
Die Aussage, dass Exceptions in C++ unschön seien, ist seit Jahren (über den genauen Zeitpunkt kann man sicher streiten) überholt. Es gab eine Zeit, in der Exceptions vergleichsweise langsam waren und insbesondere allein die Möglichkeit, dass eine Exception geschmissen worden sein könnte, einen Overhead bedeutete; das ist inzwischen hinfällig. Es gab auch eine Zeit, in der gängige C++-Programmierpraxis sich nicht allzu sehr von C unterschied, so dass eine Exception im Zweifel dauernd gefangen und wieder neu geworfen wurde, damit zwischendurch aufgeräumt werden konnte. Inzwischen kann das nicht mehr als Argument herhalten, weil (wie dir womöglich aufgefallen ist) einem der Begriff RAII praktisch andauernd um die Ohren gehauen wird. Was ja auch ganz gut ist.
Gleichwohl sind Exceptions nicht in allen Fällen das Mittel der Wahl. Grundsätzlich bedeutet eine Exception: "ich komme hier so nicht weiter, und ich nehme an, dass du es auch nicht kannst." Es wäre zum Beispiel ziemlich albern, wenn
int x; std::cin >> x;eine Exception würfe, wenn der Benutzer "abc" eingibt1. Es könnte aber programmabhängig sinnvoll sein,
program_options parse_config_file(std::istream &cfg_file) { ... if(!(cfg_file >> node_id)) throw malformed_config_file("node_id muss angegeben und numerisch sein"); ... }zu schreiben, denn man kann durchaus den Standpunkt vertreten, dass eine kaputte Konfigurationsdatei den Rest des Programms in der Regel am Weitermachen hindern wird.
Das bedeutet durchaus nicht, dass eine Exception unbedingt zum Programmende führen muss, aber wenn ich eine Exception werfe, habe ich die Vermutung, dass ein größerer Programmteil durch die Ausnahmesituation, auf die ich gestoßen bin, abgebrochen werden müssen wird.
1 Ich bin mir bewusst, dass man std::istream dazu zwingen kann, in diesem Fall eine Exception zu werfen. Ich stehe auf dem Standpunkt, dass man das nur tun sollte, wenn ein Lesefehler hinreichend fatal wäre.
-
Ist für euch eine Exception == Programmende? Oder warum soll man die nur in main fangen?
-
seldon schrieb:
eine Exception würfe, wenn der Benutzer "abc" eingibt1. Es könnte aber programmabhängig sinnvoll sein,
program_options parse_config_file(std::istream &cfg_file) { ... if(!(cfg_file >> node_id)) throw malformed_config_file("node_id muss angegeben und numerisch sein"); ... }zu schreiben, denn man kann durchaus den Standpunkt vertreten, dass eine kaputte Konfigurationsdatei den Rest des Programms in der Regel am Weitermachen hindern wird.
Das bedeutet durchaus nicht, dass eine Exception unbedingt zum Programmende führen muss, aber wenn ich eine Exception werfe, habe ich die Vermutung, dass ein größerer Programmteil durch die Ausnahmesituation, auf die ich gestoßen bin, abgebrochen werden müssen wird.
Wenn man eine fehlerhafte Konfigdatei oder Beispiel aus der Praxis, ein Savestand nicht geladen werden kann, dann kann man das aber auch ganz gut als Teil des Programms behandeln.
Also nen ganz normalen
if() ...elseBlock nehmen und den Fehler dann im else Block als Teil des normalen Programms behandeln.
Wozu brauche ich da also ne Exception?
IMO überflüssig.
-
Nun, womöglich bist du gerade zehn Funktionen tief in einem Recdesc-Parser. Wenn man mal Funktionen hat, die andere Funktionen aufrufen (ich habe mir sagen lassen, dass das gelegentlich vorkommt), ist die if-else-Fehlerbehandlungsmethode für Fälle, in denen ein ganzer Funktionskomplex abgebrochen werden soll, doch ausgesprochen unhandlich.
-
x8000 schrieb:
Ist für euch eine Exception == Programmende? Oder warum soll man die nur in main fangen?
Niemand hat gesagt dass man grundsätzlich Exceptions nur in der main fangen soll. volkard möchte seine AssertionException nur in der main fangen. Vermutlich weil die nur dann ausgelöst wird, wenn ein Programmierfehler/Logikfehler enthalten ist (Assertion eben), und dann das Programm an sich eh zum Teufel ist und nicht verlässlich weiter verwendet werden kann.
Grundsätzlich sollten Exceptions imo auf Subsystem-Ebene gefangen werden, d.h. wenn ein Subsystem einen so schwerwiegenden Fehler hat, dass es nicht sinnvoll weiterarbeiten kann, wirft es die Exception und fertig. Der Aufrufer sollte dann diese Exception fangen und entweder ordentlich behandeln oder aber eine zu seinem Subsystem passende Exception weiterwerfen.
Beispiel Parse-Fehler in der save-Datei:
Wenn der Parser komplett aussteigt, weil die Datei unkenntlich ist, schmeißt er eine SaveFileParseException. Das Modul, das den Parser aufgerufen hat, kann jetzt entweder eine Warnmeldung ausgeben á la "Save-Datei kaputt, starte leeres Dokument", wenn das angemessen ist, oder es wirft eine eigene Exception, dass es eine nötige Datei nicht laden konnte. Die Info, was beim Parsen schiefgelaufen ist, die vermutlich in der SaveFileParseException zu finden ist, interessiert an höherer Stelle vielleicht garnicht mehr.
Auf jeden Fall bedeutet das "aus dem Modul rauswerfen", dass mehrere Funktionen durchflogen werden, bevor die exception gefangen wird. Try/Catch brauchts es bei so einer Behandlung nur an Modulgrenzen. Ein grundsätzlich exceptionsicherer Code muss weder komplexer noch imperformanter als "normaler" Code sein, das bedeutet unter anderem, dass vielleicht zwei oder drei von ~100 Funktionen im Modul eine Exception werfen können, die anderen Funktionen aber nicht mit Fehlerbehandlungscode zugemüllt werden müssen. Das ist ein Gegensatz zum alten C-Errorcode Gedöns, wo erstens die Errorcodes in so ziemlich jede Schnittstelle wandern müssen, um rausgereicht zu werden, und zweitens in jeder Funktion die Errorcodes der aufgerufenen Funktionen aufgesammelt, ausgewertet und weitergereicht werden müssen.
Und selbst wenn aus irgendwelchen Gründen mein Modul einen Errorcode zurückliefern soll, dann tun das die 5-10 Funktionen, die zur äußeren Schnittstelle des Moduls gehören. Intern fliegen weiter lustig Exceptions, die an den Außengrenzen gefangen und als Errorcodes zurückgegeben werden.
Ein weiterer Vorteil von Exceptions gegenüber Errorcodes ist, dass Klienten meines Moduls sie fangen/behandeln müssen und nicht mit einem korrumpierten Programm sorglos weiterarbeiten. Ich habe schon viel zu oft C-Style ErrorCodes gesehen, die einfach ignoriert wurden. Häufig landen dann Anfragen bei mir oder Kollegen, warum "das denn nicht tut", und wir müssen dann mühsam debuggen.
@ volkard: __asm int 3 sagt mir jetzt nichts. Du schreibst, dass der Debugger an der Stelle hält. Um welchen Debugger gehts? MSVC hat eine Funktionalität, dass man beim Wurf von vorher auszuwählenden Exceptions ein break im Debugging bekommt.