Designfrage std::exception
-
-
D schrieb:
Schreib halt deine eigene Fehlerklasse! Sie muss ja nicht von exception
abgeleitet sein.So ist das aktuell auch aufgebaut. Ich hatte mir nur überlegt ob ich die Basis meiner Hirarchie von std::exception erben lassen sollte oder nicht.
www.gidf.de schrieb:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-209110.html
Ja das ist die URL dieses THREADS. UND?
-
Zumindest beim g++ (aber sicherlich auch bei anderen Compilern) hat es einen sehr praktischen Vorteil beim debuggen:
Wenn die Ausnahme nicht aufgefangen wird, dann wird am Ende des Programms exc.what() ausgegeben.
-
.filmor schrieb:
Zumindest beim g++ (aber sicherlich auch bei anderen Compilern) hat es einen sehr praktischen Vorteil beim debuggen:
Wenn die Ausnahme nicht aufgefangen wird, dann wird am Ende des Programms exc.what() ausgegeben.Jop das ist mir auch schon aufgefallen

Wie genau funktioniert dieses what() eigentlich? Eine virtuelle Funktion?
-
Wie genau funktioniert dieses what() eigentlich? Eine virtuelle Funktion?
Guck doch selbst nach!
-
Meine Programme haben in der main-Funktion immer ein "catch (const std::excpeption&)". Ich gehe davon aus, daß jede Exception von std::exception abgeleitet ist. Es ist halt ungemein hilfreich, einen Fehlertext zu erhalten, wenn etwas unvorhergesehenes geschieht.
Meine eigenen Fehlerklassen leiten von std::runtime_error ab und sind damit von std::exception abgeleitet. Std::runtime_error (gibt es eigentlich zur Laufzeit des Programmes etwas anderes als Laufzeitfehler? "catch (std::compiletime_error)"
) verwaltet einen std::string, so daß man einfach eine sinnvolle Fehlermeldung übergeben kann.Tntnet
-
tntnet schrieb:
Meine Programme haben in der main-Funktion immer ein "catch (const std::excpeption&)". Ich gehe davon aus, daß jede Exception von std::exception abgeleitet ist. Es ist halt ungemein hilfreich, einen Fehlertext zu erhalten, wenn etwas unvorhergesehenes geschieht.
Da ich mal annehme dass du das machst um ne Ausgabe zu machen á la "Exception blabla..." und danach das Programm zu beenden (vorzugsweise indem du die Exception weider wirfst), könntest du für alles andere einen cathall block hinter das erste catch schalten, um ne allgemeinere Fehlermeldung zu liefern.
(gibt es eigentlich zur Laufzeit des Programmes etwas anderes als Laufzeitfehler? "catch (std::compiletime_error)"
)Es gibt beispielsweise std::logic_error, den man z.B. im default-teil einer switch-Anweisung werfen könnte, in der man eigentlich alle Möglichketen abklappert:
enum foo{A,B}; foo f(A); /* ... */ switch (f) { case A: /*...*/ break; case B: /*...*/ break; default: throw std::logic_error("unknown value of enum foo"); }Und ja, es kann passieren, dass diese Exception geworfen wird, z.B. wenn das enum erweitert wird und jemand vergisst die switch Anweisugn zu erweitern...
-
Ja, eine runtime-Ausnahme ist was anderes als eine logic-Ausnahme. Man muß dann halt die Umkehrfrage stellen "Ist es ein Logikfehler? Nein, dann ist es ein Laufzeitfehler.".
Diese Unterscheidung finde ich schon sehr hilfreich, denn runtime_error ist eher ein Fehler auf Sprachebene und somit eher ein C++-Fehler (nenne ich jetzt mal so). Mit Laufzeitfehler ist also nicht zur Laufzeit gemeint, sondern die C++-Runtime (low level): nicht genug Hauptspeicher, Filehandler ist falsch, Pointer ist ungültig usw.
Aber es gibt ja auch Fehler, die nicht Laufzeitbedingt sind: die eher was mit der Anwendung oder Domaine zu tun haben. Diese Fehler sind nicht technisch bedingt, sondern haben eher einen fachlichen Hintergrund. Z.B. wenn jemand einen User löschen will, und am Ende stellt sich heraus, das er dafür keine Berechtigung hat. Das ist kein Lowlevel-Fehler, sondern Logik und Fachlichkeit (Domain).
tntnet! Wenn dein WebServer z.B. eine html-Datei nicht parsen kann, weil sie z.B. ein ungültiges Zeichen enthält, ist das ein logic_error (oder einer der spezialisierteren Variante).
Hier könnt ihr die Standard-Exceptions sehen.
-
Wie ist das denn mit Fehlern aufgrund von falschen Benutzereingaben (z.B. ungültige Kommandozeilen-Parameter o.ä.)? Ein runtime_error ist das ja nicht, aber doch eigentlich auch kein logic_error, oder? Der Fehler liegt schließlich nicht in meiner Programmlogik, sondern beim User, der die Hilfe-Datei nicht lesen kann

-
naja, so darfst du das ja nicht sehen. Wenn der User eine falsche Eingabe macht, gibt es einen potenziellen Kandidaten:
- std::invalid_argument (ungültiges Argument)
Und da das Passwort sicherlich als Argument übergeben wird, passt es doch schon sehr gut. Und es ist ein Logikfehler, weil dein Programm eine Logik hat... auch wenns nur ein einfaches if ist.

So wäre es nicht schlecht:
class invalid_password : public invalid_argument {};
-
Ich weiss nicht, ich würde einen logic_error eher als Fehler in der Programmlogik sehen. Die Verwendung in der Standardbibliothek bestätigt das IMHO, da invalid_argument geworfen wird, wenn man dem Zufallsgenerator zuwenig Saat gibt, oder einem bitset Zweien als Binärziffern vorsetzt.
Wenn solche Werte von der Tastatur eingelesen und ohne überprüft zu werden an die entsprechenden Funktionen übergeben werden, stimme ich zu, das ist ein Logikfehler (wenngleich der auch in der fehlenden Prüfung liegt). Wenn das Programm feststellt, dass die Benutzereingabe Schrott ist, ist das in meinen Augen ein Laufzeitfehler, der bereits vor dem Aufruf der Funktion, die den pot. Logikfehler meldet, geworfen werden sollte. Genauso wäre das invalid_password ein Laufzeitfehler, da ja kein Fehler in der Programmlogik vorliegt, wenn der Benutzer sein Passwort vergisst.
Ich selbst verwende logic_error und deren Ableitungen nur, wenn ich an der entsprechenden Stelle einen ungültigen Zustand habe, der "eigentlich" nicht eintreten dürfte.
-
logic_error ist wie assert. imho macht es nie sinn logic_error zu werfen.
-
Artchi schrieb:
naja, so darfst du das ja nicht sehen. Wenn der User eine falsche Eingabe macht, gibt es einen potenziellen Kandidaten:
- std::invalid_argument (ungültiges Argument)
Hmm... Dem Namen nach klingt das natürlich so, als würde es passen, aber eigentlich geht es bei invalid_argument doch eher um Argumente von Funktionen (also auf C++-Ebene), nicht um Benutzereingaben. Und invalid_argument ist schließlich auch von logic_error abgeleitet...
-
void login(std::string &user, std::string &password) // <- kein Argument? { if ( /* passwort mit user vergleichen */ ) { // falsch? throw invalid_password(); } }password ist natürlich kein Argument?
Ob es von Tastatur, Konfig-Datei, EXE-Parameter, WebInterface usw. kommt, ist doch egal. irgendwo wird ein Argument übergeben, in einer login-Funktion. Man muß schon abstrakter denken können, wenn programmieren will. Und wo die Exception abgefangen wird, ist der login-Funktion auch egal. Es ist ihr auch egal, wie dies dem User bekannt gegeben wird. Sie schmeisst eine Exception, weil password oder user nicht korrekt übergeben wurden.
-
Shade Of Mine schrieb:
logic_error ist wie assert. imho macht es nie sinn logic_error zu werfen.
Das liest sich, als ob es auch keinen Sinn macht, assert zu verwenden

In meinen Augen macht es sehr wohl Sinn, logic_error zu werfen, vor allem während der Entwicklung. In einer fertigen Anwendung sollte es dann nur von der Programmlogik nicht mehr möglich sein, in die Zweige zu gelangen, die einen logic_error werfen (wer hätts gedacht). Einen logic_error explizit zu fangen und zu behandeln macht indes in meinen Augen weniger Sinn, da ein Programm, dessen Programmlogik nicht mehr gewährleistet ist, nicht mehr korrekt arbeiten kann und daher auch keine logic_errors effektiv behandelt werden können (zumindest nicht ohne die Programmlogik erneut über den Haufen zu werfen).
-
Artchi schrieb:
password ist natürlich kein Argument?
Ob es von Tastatur, Konfig-Datei, EXE-Parameter, WebInterface usw. kommt, ist doch egal. irgendwo wird ein Argument übergeben, in einer login-Funktion. Man muß schon abstrakter denken können, wenn programmieren will.Natürlich ist password hier ein Argument einer Funktion. Die Frage ist hier eher die Definition von "invalid", als die von "argument". Ist ein Passwort jetzt logisch gesehen "valid", wenn es ein gültiges Passwort ist (z.B. keine ungültigen Zeichen - welche das auch immer sein sollten - enthält), oder nur wenn es das zum Benutzer passende Passwort ist? Und genau in dieser Frage würde ich ersteres eher als Logikfehler und zweiteres als Laufzeitfehler einordnen. Dass man hier generell eine Exception einsetzen kann, bestreite ich garnicht.
-
invalid_argument bedeutet ein logik error. ein logik error ist etwas dass nie auftreten darf wenn der code korrekt ist.
sprich: einen NULL zeiger als Passwort uebergeben ist ein invalid_argument. ein falsches passwort ist ein runtime_error ein fehler der _immer_ auftreten kann egal wie perfekt der code ist. (zB file not found, security exceptions,...)
logic_errors zu werfen macht deshalb keinen sinn, weil man so logik fehler verstecken kann. ein logikfehler hat eliminiert zu werden. er darf in der release version nicht mehr vorhanden sein. wenn ich aber logic_error werfe, dann kommt das 1. nicht immer bei mir an, weil er bei vielen catches einfach gefangen wird und ich kann explizit logik errors ignorieren.
beide situationen sind furchtbar. ein assert dagegen, kann ich weder ignorieren noch uebersehen. und vorallem: ein assert kann teure operationen ausfuehren um zu sehen ob der code korrekt ist (zB kann assert nach jedem insert in einem baum testen ob der baum gebalanced ist).
-
Artchi schrieb:
Diese Unterscheidung finde ich schon sehr hilfreich, denn runtime_error ist eher ein Fehler auf Sprachebene und somit eher ein C++-Fehler (nenne ich jetzt mal so). Mit Laufzeitfehler ist also nicht zur Laufzeit gemeint, sondern die C++-Runtime (low level): nicht genug Hauptspeicher, Filehandler ist falsch, Pointer ist ungültig usw.
Hast du dafür eine Quelle? Im Moment tendiere ich doch eher dazu, das falsche Passwort (um mal bei dem Beispiel zu bleiben) als runtime_error anzusehen, denn ein logic_error ist es jedenfalls definitiv nicht. Warum sollte runtime_error für Fehler innerhalb der Runtime-Library reserviert sein, bzw. wo steht das?
-
19.1.1/1 schrieb:
The class logic_error defines the type of objects thrown as exceptions to report errors presumably detectable before the program executes, such as violations of logical preconditions or class invariants.
19.1.6/1 schrieb:
The class runtime_error defines the type of objects thrown as exceptions to report errors presumably detectable only when the program executes.
Der Standard sieht das alles genauso wie Shade Of Mine.