Error handling: Exception vs. Error return value



  • dirtydynamite schrieb:

    Auch fast alles IO, außer Timer.

    Mute mir nicht zu, alle WinAPI-Funktiionen aufzuzählen, die einen BOOL oder einen HANDLE, der INVALID_HANDLE_VALUE werden könnte, oder inen Zeiger, der NULL werden könnte, zurückgeben, aufzuzählen. Die meisten davon sind nicht IO, fürchte ich.



  • assert: "doofer programmierer hat nen blöden Fehler gemacht"
    error value: "hab den font nicht gefunden, das teile ich dir mit, vielleicht geht das ja noch zu fixen"
    exception: "subkomponente hat den font nicht gefunden. Hab keinen fallback. geordneter rückzug ist angesagt."



  • assert: "doofer programmierer hat nen blöden Fehler gemacht"
    error value: "hab den font nicht gefunden, das teile ich dir mit, vielleicht geht das ja noch zu fixen"
    exception: "subkomponente hat den font nicht gefunden. Hab keinen fallback. geordneter rückzug ist angesagt."

    👍 👍 👍



  • otze schrieb:

    assert: "doofer programmierer hat nen blöden Fehler gemacht"
    error value: "hab den font nicht gefunden, das teile ich dir mit, vielleicht geht das ja noch zu fixen"
    exception: "subkomponente hat den font nicht gefunden. Hab keinen fallback. geordneter rückzug ist angesagt."

    Passt.
    Leider kommen bei mir 99% asserts an. 🤡



  • volkard schrieb:

    dirtydynamite schrieb:

    Auch fast alles IO, außer Timer.

    Mute mir nicht zu, alle WinAPI-Funktiionen aufzuzählen, die einen BOOL oder einen HANDLE, der INVALID_HANDLE_VALUE werden könnte, oder inen Zeiger, der NULL werden könnte, zurückgeben, aufzuzählen. Die meisten davon sind nicht IO, fürchte ich.

    Drei bis fünf würden mir reichen, wenn sie nicht alle in die gleiche Kategorie fallen.



  • wenn man exceptions verwendet, sollte man aber auch immer daran denken, exceptionsafe zu programmieren

    http://www.google.de/#hl=de&tbo=d&sclient=psy-ab&q=c%2B%2B+exceptionsafe



  • -.- da ist man einen Nachmittag mal nicht im Forum unterwegs, schon muss mans ich seitenlange Threads durchlesen. Aber gut dass jemand fragt, sonst hätte ich das in naher Zukunft getan.

    Zum ersten: Schon wieder kommt hier das typische Internetverhalten zum tragen. Man guckt auf 3 Websites nach einem Thema und kriegt 5 verscheidene Meinungen, 10 verschiedene Anweisungen und 20 verschiedee Best-Practices. 😣

    Auf der einen Seite gibt es die Bücher wie Herb Sutters Exceptional C++ Reihe, die einen lehrt, exceptionsicher zu coden. Dann kommen Leute wie cooky an und meine, ist eh alles sinnlos, weil es bei asynchronen Operationen brennen wird wie die Hölle.

    (Dazwischen möchte ich kurz mal reinwerfen, ich denke die meisten hier, gerade auch die, die schon länger angemeldet sind und etwas mehr als 10 oder 20 Posts haben, kennen sich untereinander virtuell und man kann auf eren [begründendete] Meinung was geben. Also nicht angegriffen fühlen, z.B., denn die meisten hier haben's echt drauf!)

    Zum Thema:

    pumuckl schrieb:

    Tomahawk schrieb:

    Das schöne bei den Exceptions schein mir, dass die Exception den Stack abbaut?!

    Ja, und:

    - dass man einer Exception mehr Info mitgeben kann als einen Errorcode
    - dass Funktionen, die Exceptions werfen, einen echten Rückgabewert haben können, den man nicht erst aus einem struct (Rückgabewert + Errorcode) raisfriemeln muss
    - dass man bei Exceptions nicht nach jeder Funktion den Rückgabewert abfragen und behandeln muss sondern alle kritischen Funktionsaufrufe in einen try-Block packt (oft ist es irrelevant, welche Funktion genau schiefgelaufen ist)
    - dass man Exceptions nicht aus versehen ignorieren kann wie z.B. Fehlercodes.

    Das hat pumuckl in dem anderen Thread geschrieben. Ich denke, wir sind uns einig, dass dies alles nichts neues ist. Das sind ja auch die Gründe und der Sinn von Exceptions.

    Die Frage ist jetzt wann sie genutzt werden sollten, dazu sollte man sich auch erstmal klar machen, was es für Fehlerklassen gibt:

    otze schrieb:

    assert: "doofer programmierer hat nen blöden Fehler gemacht"
    error value: "hab den font nicht gefunden, das teile ich dir mit, vielleicht geht das ja noch zu fixen"
    exception: "subkomponente hat den font nicht gefunden. Hab keinen fallback. geordneter rückzug ist angesagt."

    Der erste Fall: Hier hat der Programmierer versagt, er hat vielleicht eine Division durch 0 zugelassen oder den Bereich eines Arrays überschritten, auch Pointerfehler gehören hier zu.
    Das geht aber dann soweit, was ist wenn der Benutzer z.B. den Divisor einer Division eingibt?! Im Debug-Modus wird man darauf aufmerksam gemacht, aber dann?
    Hier ist man als Programmierer trotzdem gefragt und muss sicherstellen, dass der Nutzer keinen Müll eingibt.

    Der zweite Fall: Hier ist quasi der schwächere Fall von Exceptions anzutreffen, otze gibt das gut wieder indem er sagt: "geht grad nicht, aber vielleicht ist später noch was zu retten". Diese Methdoe wäre innerhalb des in 3. genannten Subsystems anzutreffen.
    Aber wieso wird hier keine Exception geworfen?

    Der dritte Fall: Methoe 2 ist angewandt worden und es failte, die ImageLoader-Klasse hat keine Ahnung wie sie das nun handeln soll, aber sie wird benutzt, und daher wirft sie eine Exception.

    Ich finde das gesamte Thema immer noch total unausgereift. Überall wird einem geraten "nix, nein, nie exceptions werfen", aber überall wird auch gesagt "ja, wirf, lass es raus alles". Java kommt ja dann an und wirft vieles, aber lässt vieles implizit durchgehen (Checked/Unchecked Exceptions, auch wenn das ja ein anderes Konzept ist).

    Dazu kommt dann noch, dass es Exception-Specifications gibt, diese aber so sclecht sind, dass sie nicht verwendet werden sollten oder sogar vom Compiler ignoriert werden. Warum gibt es dann sowas?

    Aber was jeder für sich (bzw. mit seinem Entwicklungsteam) machen sollte, ist zu bedenken und zu planen ob und wie wann Exceptions geworfen werden sollten.



  • Skym0sh0 schrieb:

    Auf der einen Seite gibt es die Bücher wie Herb Sutters Exceptional C++ Reihe, die einen lehrt, exceptionsicher zu coden. Dann kommen Leute wie cooky an und meine, ist eh alles sinnlos, weil es bei asynchronen Operationen brennen wird wie die Hölle.

    Hey, hey, moment mal. Ich habe nirgends geschrieben, dass man nicht exception-sicher coden sollte! Ich sehe Exception-Sicherheit aber mehr als einen Nebeneffekt von gutem Code an - wenn alle Besitzverhältnisse klar sind (RAII und so), und man immer hübsch einen RAII-Container pro Ressource anlegt, dann ist das automatisch auch exception-safe.*
    Der Punkt ist, dass der eigentliche Grund warum Exceptions geworfen werden immer weniger vor kommt, je größer und zuverlässiger das Programm wird. Man hat ein Mini-Programm geschrieben dass ein Fenster öffnet auf dem drei Buttons sind? Jetzt failt CreateWindow? Wunderbar, Exception + exit. Was soll man auch sonst machen. Wenn man jetzt aber ein Programm mit 3 Fenstern und 20 Sub-Fenstern hat bei dem alles asynchron läuft und irgendwo kann da jetzt ein Fenster nicht geöffnet werden (oder eine Verbindung nicht hergestellt werden, was auch immer), ist das wirklich ein Grund große Teile des Codes zu überspringen? Eher nicht, das Programm bei so etwas mal eben weit zurückfallen zu lassen wäre wohl schwachsinn, der ganze Rest funktioniert doch noch. Ein großes Argument für Exceptions das ich auch immer wieder lese ist die größere Menge an Fehlerbeschreibung die man da rein bekommt. Hey, wenn man das unbedingt braucht: Man muss Exceptions ja nicht werfen! 😃 Siehe z.B. Alexandrescus Expected<T>, durchaus ein interessantes Konzept. Noch schöner wäre allerdings, wenn der Compiler erkennen könnte, dass keine Exception geworfen werden kann, wenn man selbst kontrolliert.

    *Außer bei so etwas:

    foo(smart_ptr(new T), smart_ptr(new T));
    

    Daher: Immer schön make_smart_ptr nutzen! 🤡



  • Oh sorry, nein so meinte ich das eigentlich auch nicht.

    Mich stört die gegenläufige Meinung und die verscheidenen "Anweisungen". Klar, dein Argument ist vollkommen berechtigt. Aber aus dem Grund, waurm es diese Bücher gibt, lässt sich schonmal ableiten, dass Exceptions auch genutzt werden sollen. (Den umgekehrten Fall dass Dinge die beschrieben werden, nicht genutzt werden sollen habe ich noch nicht gesehen, oder hat einer von euch ein Buch über Exception Spcifications?)

    Ok, cooky: Du sagst also, dass durch bessere Programmierung Exceptions unnötiger werden, weil sie einfach nicht gebraucht werden?

    Und wer garantiert die von dir erwähnte Zuverlässigkeit?

    und: RAII garantiert zwar Exceptionssicherheit, aber welche? Die starke, die schwache?
    Naja, die Garantien sollen hier mal außen vor gelassen werden, denn die starke Garantie kann man bei performancekritischen Dingen fast eh nie verwenden.



  • Skym0sh0 schrieb:

    Ok, cooky: Du sagst also, dass durch bessere Programmierung Exceptions unnötiger werden, weil sie einfach nicht gebraucht werden?

    Nein. Ich sage dass die Fälle in denen Exceptions sinnvoll sind, einfach immer weniger werden je größer das Programm wird.

    Skym0sh0 schrieb:

    Und wer garantiert die von dir erwähnte Zuverlässigkeit?

    Wann? Wo? Hä?

    Skym0sh0 schrieb:

    und: RAII garantiert zwar Exceptionssicherheit, aber welche? Die starke, die schwache?

    RAII kann natürlich erst mal nur garantieren dass nichts leakt. Bei stärkeren Garantien muss man dann halt doch noch ab und zu mal nachdenken, fürchte ich. Da machen Exceptions einem das Leben aber eher schwerer als leichter. 😉



  • Ok, dann sind wir uns einig, bei den letzten 2 Punkten.

    Mh, das Programm wird größer, aber weniger Exceptions sind nötig? Wie das?
    Dass solche komplexen Zusammenhänge meist nicht linear sind ist mir klar, aber dass weniger Exceptions nötig sind?!?? Oo



  • Der Punkt ist, dass man eine Exception werfen will, wenn irgendetwas wirklich kaputt ist. Wenn deine Anwendung genau ein Fenster öffnet und das nicht öffnen kann, dann ist etwas wirklich kaputt. Wenn deine Anwendung das 50. Fenster nicht öffnen kann, ist das nicht der Fall.



  • Wieso?

    Da stimmt doch genauso wenig etwas...



  • Ja, aber du willst nur ne Fehlermeldung anzeigen und nicht wirklich viel Code im gleichen oder höheren Scope überspringen.



  • Kannst du mir einen anderen Fall nennen, als den mit den vielen Fenstern?

    Wie gehst du z.B. vor wenn du nach etwa 200h Laufzeit deines fehlerfreien Programms auf einmal ein vorher nicht benötigtest Bild (oder Datei) laden musst, dies aber nicht geht (sei es, weil die Datei fehlt)?

    Im ersten Fall, dass der User gesagt, dass er genau diese Datei möchte: Gibst du dem dann schnell mal eine MessageBox mit "lololol lern ma richtig zu arbeiten du n00b" (oder etwas vergleichbares)?

    Der zweite Fall: Dein Programm braucht diese Datei (hat die Integrität aller benötigten Dateien vielleicht schon beim Programmstart positiv gecheckt) dringendst, es soll aber auch nicht so ohne weiteres abstürzen...



  • Kann das Programm ohne die Datei keine weitere Funktion mehr erfüllen? Bau mal ein konkretes Beispiel. Wenn der User einfach nur eine Datei öffnen möchte, würde ich jedenfalls nichts werfen. Ein Beispiel dass sich nicht auf Fenster bezieht? Sockets. Ein Server der gerade mit 10k Clienten verbunden ist, will nicht wirklich viel mehr als einen log-Eintrag machen wenn er nicht noch einen Socket erstellen kann. Wenn ein send/recv fehl schlägt vielleicht noch nicht einmal das.



  • Du musst auch bedenken das cooky451 noch Student ist und dementsprechend nur an "Mini"-Projekten mitgearbeitet hat. Deswegen beurteilt er alles aus einem anderem, eher naiven, Blickwinkel.



  • Fall 1, der einfache:

    #include <iostream>
    #include <fstream>
    #include <string>
    
    void performFileOp(istream & is)
    {
    	int x;
    	is >> x;
    
    	cout << x << endl;
    }
    
    int main()
    {
    	string filename;
    	getline(cin, filename);
    
    	ifstream f(filename);
    
    	if ( !f )
    		cerr << "L O L O L O L, nimm ne richtige Datei!" << endl;
    	else
    		performFileOp(f);
    
    	return 0;
    }
    

    Zweiter Fall, mh kein gutes Beispiel:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <iterator>
    #include <vector>
    #include <algorithm>
    
    double calc(double x)
    {
    	vector<double> lookup;
    	{
    		ifstream lookUpf("LookupTable.txt");
    
    		if ( !lookUpf )
    			throw "fuuuuuuuuuuu"; // oder was?
    
    		copy(istream_iterator<double>(lookUpf), istream_iterator<double>(), back_inserter(lookup));
    	}
    
    	return lookup[x];
    }
    
    int main()
    {
    	{
    		ifstream integrityCheck("LookupTable.txt");
    		if ( !integrityCheck )
    			return -1;
    	}
    
    	cout << "Wurzel aus ";
    	double x;
    	cin >> x;
    
    	cout << calc(x) << endl;
    
    	return 0;
    }
    

    (Der Sinn sei mal dahingestellt oder ob das so gut ist...)



  • bgx schrieb:

    Du musst auch bedenken das cooky451 noch Student ist und dementsprechend nur an "Mini"-Projekten mitgearbeitet hat. Deswegen beurteilt er alles aus einem anderem, eher naiven, Blickwinkel.

    Wer sagt, dass ich das nicht bin?
    Und wieso sollte er nur kleine Projekte gesehen haben?

    Und seine Meinung soll er haben, aber er kann sie mir erklären und seine Beweggründe gleich mit. Ich hoffe nämlich, dass ich das verstehe und dann merke: "Ey, das is ja voll cool. Wenn ich das ab jetzt so mache, krieg ich alle Mädels ab"

    ...



  • Code als Beispiel meinte ich eigentlich eher weniger. Nur halt etwas durchdachter. Hier erfüllen beide Programme ja nur genau einen Zweck, also letztlich ist es kaum relevant, wie man den Fehler transportiert. Damit das relevant wird, brauchen wir irgendetwas wo man viel macht. Ein Beispiel wäre z.B. ein (relativ sinnloses) Programm, dass ab einem Pfad alle Dateien durch geht und deren Hashes berechnet. Hier macht man wohl einen Thread, der einfach die Verzeichnisse durchläuft und alle Dateinamen in eine queue pusht. Aus der lesen dann mehrere Threads, öffnen die Dateien, lesen, berechnen ihren Hash, etc. Jetzt kann eine Datei nicht geöffnet werden. Will man an dieser Stelle wirklich viel Code überspringen? (aka eine Exception werfen?) Eher nicht. Man schreibt einfach irgendwo rein "sorry, ging nicht" und macht weiter. Im besten Fall versucht man es später noch mal.


Anmelden zum Antworten