Fehlerbehandlung in der Praxis - Errorcode, Exception, was besseres?
-
Wann sollte man was für die Fehlerbehandlung nehmen? Jetzt bitte keine theoretischen Aussagen, sondern echte Beispiele. Was sollte man z.B. bei Fehleingaben des Benutzers machen? Gibts noch was anderes als Errorcodes und Exceptions?
-
Fehleingaben sind keine Ausnahme, sie sind die regel. eine Fehleingabe kann immer passieren, und deswegen werden fehleingaben direkt vor ort behoben, meistens wird die Eingabe erneut angefordert. Wenn es sich um eine eingabedatei handelt, kann man nachträglich immernoch eine Exception werfen, wenn man nicht weis, wie es weitergehen soll.
Ein Beispiel in C++ für fehleingaben:
int i; while(!(cin>>i)) { cin.clear();//eventuell noch ein ignore um den puffer komplett zu leeren cout<<"Bitte geben sie eine Zahl ein": }
Eine lösung mit exceptions wäre hier sicher schwieriger
-
Wenn man es so macht, besteht aber wieder das Problem, das jede GUI das machen muss, wenn man sie austauschen will oder mehrere hat.
-
Die Antwort darauf lautet: Beides.
Wenn man alles mit Exceptions macht läuft auch etwas falsch. Erst im Zusammenhang kann man entscheiden ob man besser Excpetions benutzt oder if-abfragen.
Nehmen wir mal die einfache Prüfung eines pointers auf NULL. Ohne exceptions könnte das so aussehen:
if (p != NULL ) { p->doIt(); } else { }
Ebenso könnte man aber auch schreiben:
try { p->doIt(); } catch (...) { }
Beide Ansätze würden zum gleichen Verhalten führen. Welcher Ansatz ist jetzt aber "besser"? Die Antwort darauf ist: Kommt drauf an.
Und zwar kommt es darauf an ob p = NULL ein normaler Zustand von p sein kann oder ob p = NULL eine Ausnahme ist die normalerweise nicht auftreten sollte.
Im ersteren Fall ist die Variante mit dem if die bessere Wahl aus zwei Gründen:
-
Durch den if/else block sage ich als Programmierer indirekt aus, daß ich an der Stelle den Zustand p=NULL als möglichen Zustand erwarte, dieser Zustand also kein Fehlerfall ist.
-
Es ist performanter als die Exceptions. Da p=NULL ein normaler ZUstand ist kann er häufiger auftreten und der Exception Ansatz würde dann unnötig Performance kosten. (Weil Exceptions mehr Performance schlucken als eine einfache if-abfrage)
Anders ist das wenn ich p=NULL als Ausnahme betrachte, es also in 99% der Fälle immer p!=NULL ist. In dem Fall ist der Exception Ansatz der besserer und wieder aus zwei Gründen:
-
Durch den try/catch block sage ich explizit das der Fall p=NULL ein Fehlerfall ist, also normal nicht auftreten sollte.
-
Es ist performanter weil ich in 99% der Fälle eine unnötige if-Abfrage komplett spare und nur im Fehlerfall eine Exception ausgelöst wird.
In beiden Fällen rede ich davon das die Wahl der Implementierung eine indirekte "Meta"-Aussage mit sich bringt, eine Information die für spätere Wartung oder andere Programmierer wichtig ist. Auch ohne es Kommentieren zu müssen kann ich durch den korrekten Einsatz von Exceptions festlegen, welche Zustände ich als Fehler, welche ich als normalen Programmablauf betrachte.
-
-
loks, du bist komplett auf dem Holzweg, weil ein Nullpointerzugriff undefiniertes Verhalten auslöst. Vielleicht sprichst du von Windows-SEH, aber das ist nicht das, was man normalerweise meint, wenn man von Exceptions spricht.
Die Detektion dieses Fehlerzustandes wäre in beiden Fällen gleich, nur die Art der Übermittlung ändert sich:if (p != 0) { ... } else { return FEHLER; } // vs. if (p != 0) { ... } else { throw Fehler(); }
Gibt man Fehlercodes zurück, ist der direkte Aufrufer dafür verantwortlich, ihn auszuwerten. Tut er das nicht, Pech gehabt. Bei Exceptions kann auch ein indirekter Aufrufer die Exception fangen, außerdem kann die Exception nicht (unabsichtlich) ignoriert werden. (Weiterer kleiner Vorteil: Exceptions können auch bei Funktionen, deren Rückgabetyp keine Fehlercodes zulässt und bei Konstruktoren angewendet werden)
Die Unterscheidung in erwartete und unerwartete Ausnahmen würde ich auch so treffen, allerdings ist nicht so ganz klar, wo man da die Trennlinie zieht. Alles was in irgendeiner Weise von Benutzereingaben abhängt ist sicher ein erwarteter Fehler (Syntaxfehler in der Eingabe, angegebene Datei existiert nicht usw.). Aber wann Exceptions?
-
sofa schrieb:
Wenn man es so macht, besteht aber wieder das Problem, das jede GUI das machen muss, wenn man sie austauschen will oder mehrere hat.
Das ist aber genau das was du willst, denn je nach GUI ist die Eingabemaske völlig anders strukturiert, evt. sogar das Eingabeformat und dementsprechend weiß nur der Code der die GUI-Logik enthält und die GUI aufbaut wie er die Daten richtig und sinnvoll validieren kann.
Sollte das nicht notwendig sein, dann kann man den Code zur Validierung auslagern, so dass alle Oberflächen ihn gemeinsam nutzen können, es sagt doch niemand, dass du den Code mehrfach schreiben musst, nur weil du vor Ort prüfst.
-
Wiss0r schrieb:
Sollte das nicht notwendig sein, dann kann man den Code zur Validierung auslagern, so dass alle Oberflächen ihn gemeinsam nutzen können, es sagt doch niemand, dass du den Code mehrfach schreiben musst, nur weil du vor Ort prüfst.
Und was macht die Validierung im Fehlerfall, Exception oder Errorcode? Darum gehts.
-
sofa schrieb:
Wiss0r schrieb:
Sollte das nicht notwendig sein, dann kann man den Code zur Validierung auslagern, so dass alle Oberflächen ihn gemeinsam nutzen können, es sagt doch niemand, dass du den Code mehrfach schreiben musst, nur weil du vor Ort prüfst.
Und was macht die Validierung im Fehlerfall, Exception oder Errorcode? Darum gehts.
Die Validierung weiß was schief gelaufen ist, da es sich um Benutzereingaben handelt in otzes Posting, sollte sie eine Fehlerbeschreibung in textueller Form zur Verfügung stellen und einfach über einen booleschen Rückagetyp signalisieren, dass die Daten nicht erfolgreich validiert werden konnten.
Beim Einlesen von Dateien kommt es darauf an, wenn wichtige Dateien der Anwendung korrupt sind, dann ist es durchaus in Ordnung sich mit einer Exception zu verabschieden. Kommt die Datei vom Benutzer sollte er möglichst genau darüber informiert werden was schief gelaufen ist (natürlich so dass er versteht was gewollt ist).
-
im falle einer gui steht dir noch ein dritte weg offen: messages.
will heißen, du stellt den fehler fest und statt ihn versuchen vorort zu erledigen, erzeugst du eine message, dass etwas schiefgegangen ist. dann muss die nur noch jemand fangen. hier mal am bsp von qt mit signal-slot und dem validieren eines eingabetextes (value). die variable msg hängt dann vom validator ab. also bswp "Not a number." oder ähnliches:... int pos; if (validator.validate(value,pos)==QValidator::Invalid) { emit(validationFailedSignal(msg,value,pos)); } ... [irgendwo der validationFailedSlot als bsp hier eine msgbox] void validationFailedSlot(QString msg,QString value, int error_pos) { QMessageBox::warning(parent, tr("Validation Error"), QString(tr("%1\nIn "%2" at Position: %3")).arg(msg).arg(value).arg(error_pos)); }
andere varianten als die messagebox sind auch denkbar. man könnte bspw eine fix probieren, den wert zurückzusetzen oder sonst etwas machen. der vorteil an der message ist, dass man die fehlerbehandlung von der eigentlichen fehlererkennung trennen kann und so beides einfacher recyclen kann.
-
Bashar schrieb:
loks, du bist komplett auf dem Holzweg,
Sagt der der das Prinzip eines BEISPIELS nicht begriffen hat. Damit zeigt sich nur wieder as hier ernstgemeinte Diskussion unmöglich ist.
-
loks schrieb:
Sagt der der das Prinzip eines BEISPIELS nicht begriffen hat. Damit zeigt sich nur wieder as hier ernstgemeinte Diskussion unmöglich ist.
Wenn man keine Ahnung hat aber auch nicht :p
-
Alles was in irgendeiner Weise von Benutzereingaben abhängt ist sicher ein erwarteter Fehler (Syntaxfehler in der Eingabe, angegebene Datei existiert nicht usw.). Aber wann Exceptions?
Exceptions würde ich werfen, wenn ein Fehler auftritt, der eigentlich nicht auftreten sollte, und bei dem ich den geordneten Programmablauf nicht mehr garantieren kann. Zum Beispiel wenn sich die Hardware weigert das zu tun was man will, oder wenn aufeinmal die verbindung zu einem Server weg ist.
Anderes Beispiel können parser sein. Wenn irgendwo tief unten im parser callstack ein Fehler passiert, ist es einfacher mit einer exception rauszuspringen, und dann auf oberster Ebene zu fangen. Also sowas:
bool parseData(string data) { try { doParse(data); return true; } catch(ParseException& e) { cerr<<e.message; return false; } }
Das macht den Parser selbst direkt viel schöner, weil sich höherlevelige funktionen nicht um den status der unteren zu kümmern brauchen. Sie könnten wahrscheinlich eh nicht sonderlich viel daran ändern.
-
otze schrieb:
Alles was in irgendeiner Weise von Benutzereingaben abhängt ist sicher ein erwarteter Fehler (Syntaxfehler in der Eingabe, angegebene Datei existiert nicht usw.). Aber wann Exceptions?
Exceptions würde ich werfen, wenn ein Fehler auftritt, der eigentlich nicht auftreten sollte, und bei dem ich den geordneten Programmablauf nicht mehr garantieren kann. Zum Beispiel wenn sich die Hardware weigert das zu tun was man will, oder wenn aufeinmal die verbindung zu einem Server weg ist.
Anderes Beispiel können parser sein. Wenn irgendwo tief unten im parser callstack ein Fehler passiert, ist es einfacher mit einer exception rauszuspringen, und dann auf oberster Ebene zu fangen. Also sowas:
bool parseData(string data) { try { doParse(data); return true; } catch(ParseException& e) { cerr<<e.message; return false; } }
Das macht den Parser selbst direkt viel schöner, weil sich höherlevelige funktionen nicht um den status der unteren zu kümmern brauchen. Sie könnten wahrscheinlich eh nicht sonderlich viel daran ändern.
Das sind meist genau die Fälle wo man in C goto benutzen muss(te) und hier finde ich sind Exceptions auch der einzig sinnvolle Weg, da alles aufgeräumt wird (wenn man sauber gearbeitet hat) und die entsprechende Methode sofort verlassen wird.
Was ich auch einen unschätzbaren Vorteil an Exceptions finde: man kann die programmtechnische Identifikation des Fehlers und die textuelle für den Benutzer koppeln. In C sieht es ja meist so aus, dass man eine Fehlernummer zurückgibt und diese dann erst mühsam übersetzen muss.
-
Ich finde ein wesentlicher Vorteil von Exceptions sind, dass sie im unguenstigsten Fall das Programm einfach beenden. Wenn man einen Fehlercode ignoriert, dann laeuft das Programm weiter im undefinierten Zustand und das ist das Schlimmste was passieren kann.
Gibt es aber noch andere Techniken ausser Exceptions und Fehlercode fuer die Fehlerbehandlung?
-
DEvent schrieb:
Gibt es aber noch andere Techniken ausser Exceptions und Fehlercode fuer die Fehlerbehandlung?
Fehlercallbacks, sowas wie new_handler o.ä. Das kann man vielleicht einsetzen, wenn Exceptions nicht zur Verfügung stehen, wenn man auf C als gemeinsamen Nenner zurückgreifen muss.
Common Lisp Conditions. Von der Idee her sowas wie Exceptions, aber irgendwie auch nicht, weil der Handler vor dem Stack-Unwinding eingreifen kann.