Error handling: Exception vs. Error return value



  • 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.



  • Wenn du bei Programmstart die existenz aller benötigten Dateien geprüft hast und irgendein Vollpfosten löscht sie dir danach unterm Hintern weg, dann wirf eine pebkac-Exception und gut is'.
    Erstes Beispiel ist irgendwie komisch. In diesem simplen Fall natürlich if/else und gut isses. Anders sieht's aus, wenn nicht nur das Öffnen der Datei fehlschlagen kann, sondern auch zig darauf folgende Operationen. Da gebietet imho schon allein die Ästhetik try/catch.



  • cooky451 schrieb:

    Der Punkt ist, dass der eigentliche Grund warum Exceptions geworfen werden immer weniger vor kommt, je größer und zuverlässiger das Programm wird.

    Hä? Du programmiertst, daß weniger oft der SPeicher ausgeht, die Platte besser ist und der Benutzer die CD nicht rausnimmt?

    cooky451 schrieb:

    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.

    Soweit ok.

    cooky451 schrieb:

    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.

    Sagt ja keiner, daß die Exception erst in der main gefangen wird.

    cooky451 schrieb:

    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.

    Das teile ich nicht.

    cooky451 schrieb:

    Hey, wenn man das unbedingt braucht: Man muss Exceptions ja nicht werfen! 😃 Siehe z.B. Alexandrescus Expected<T>, durchaus ein interessantes Konzept.

    Mal schauen.

    cooky451 schrieb:

    Noch schöner wäre allerdings, wenn der Compiler erkennen könnte, dass keine Exception geworfen werden kann, wenn man selbst kontrolliert.

    Die gehts jetzt aber hoffentlich nicht nur um Performance.



  • cooky451 schrieb:

    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.

    *meld* Das Muster kenne ich:
    Du mit Deinen kleinen Programmen kannst ja schreiben, wie Du willst. Aber echte Entwickler, diue echt große Programme schreiben, verwenden natürlich UML. Wer das nicht tut, der ist ein kleiner Zwerg.

    Ebenso muß man UN benutzen, sobald das Programm mal mehr als 20 Mannjahre braucht. Wer UN nicht benutzt, der hat null Ahnung.

    Nur mit Struktogrammen kann man einigermaßen fehlerfrei planen. Wobei fehlerfreie Programme gibt es eh nicht, das ist mathematisch bewiesen.

    Da verweise ich dann aber auf Fachliteratur: http://de.wikipedia.org/wiki/Des_Kaisers_neue_Kleider



  • cooky451 schrieb:

    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.

    Jo, aber wenn der Socket nicht erstellt werden kann, dann hab ichj zwei Möglichkeiten: Untebleiben und noch ein paarmal probieren, oder hochgehen und oben einfach weitermachen, der nächste CLient wird schon wieder einen Socket anlegen können. Wie weit hoch gehen? Ist mir unten völlig egal!!! Das erledigen Exceptions auf feinste Weise.



  • cooky451 schrieb:

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

    Falsch!
    Der Benutzer hat "Connect" gedrückt. Ich mache Sockets auf, lese Config-Files, dabei steig ich doch schon drei Funktionslevel runter oder mehr. Und dann klappt was nicht. Also mache ich kein normales Fenster auf, sondern zeige ihm einen Fehlerdialog. Und zwar, indem die Exception ungefähr in der Höhe gefangen wird, wo der Benutzer den "Connect"-Knopf gedrückt hat.
    Und? Die anderen 49 Sitzungsfenster bleiben davon doch unberührt und laufen normal weiter. Ob der DIalog modal angezeigt werden soll oder nicht? Hast freie Auswahl.



  • Das heisst, so in etwa würde das aussehen, Pseudocodemäßig

    vector<Hash> hashings;
    
    void calcHash(Path p)
    {
    	File f;
    	f.open(p);
    
    	Hash h = calcHashImpl(f);
    
    	hashings.push_back(h);
    }
    
    int main()
    {
    	Path path = getPath();
    
    	Threads = createThreads(5);
    
    	Queue q;
    
    	Thread[0].run( traversePathAndPushBack(path, q) );
    
    	while ( !q.empty() )
    	{
    		Thread t = getNextFreeThread(Threads);
    
    		t.run( calcHash(q.next()) );
    	}
    
    	return 0;
    }
    

    Dann wäre das hier in etwa deine Version:

    vector<Hash> hashings;
    
    void calcHash(Path p)
    {
    	File f;
    	f.open(p);
    
    	Hash h = calcHashImpl(f);
    
    	if ( !h.failed() )
    		hashings.push_back(h);
    }
    
    int main()
    {
    	Path path = getPath();
    
    	Threads = createThreads(5);
    
    	Queue q;
    
    	Thread[0].run( traversePathAndPushBack(path, q) );
    
    	while ( !q.empty() )
    	{
    		Thread t = getNextFreeThread(Threads);
    
    		t.run( calcHash(q.next()) );
    	}
    
    	return 0;
    }
    

    Und das hier die Version mit Exceptions:

    vector<Hash> hashings;
    
    void calcHash(Path p)
    {
    	File f;
    	f.open(p);
    
    	try {
    		Hash h = calcHashImpl(f);
    		hashings.push_back(h);
    	}
    	catch ( ... )
    	{
    		cerr << "geht nicht" << endl;
    	}
    }
    
    int main()
    {
    	Path path = getPath();
    
    	Threads = createThreads(5);
    
    	Queue q;
    
    	Thread[0].run( traversePathAndPushBack(path, q) );
    
    	while ( !q.empty() )
    	{
    		Thread t = getNextFreeThread(Threads);
    
    		t.run( calcHash(q.next()) );
    	}
    
    	return 0;
    }
    

    Huch, auf einmal mischen sich noch Menschen hier ein ?! Oo

    Die Hälfte von volkards Posts verstehe ich wieder nicht 😣
    Und Sarkasmus/Ironie kommt im Interwebz meistens scheisse, weil mans nicht unbedingt versteht



  • Was dem Linuxer seine Distribution ist dem C++ler seine exception!
    In meinem aktuellen Projekt, das nur so vor Benutzer-abhängigen Dingen (Nein, keine Pille-Palle-Stringfelder die man mal so fix auf validität prüft) strotzt, wüsste ich nicht, wie ich ohne exceptions Herr der Lage würde...



  • volkard schrieb:

    cooky451 schrieb:

    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.

    *meld* Das Muster kenne ich:
    Du mit Deinen kleinen Programmen kannst ja schreiben, wie Du willst. Aber echte Entwickler, diue echt große Programme schreiben, verwenden natürlich UML. Wer das nicht tut, der ist ein kleiner Zwerg.

    Ebenso muß man UN benutzen, sobald das Programm mal mehr als 20 Mannjahre braucht. Wer UN nicht benutzt, der hat null Ahnung.

    Nur mit Struktogrammen kann man einigermaßen fehlerfrei planen. Wobei fehlerfreie Programme gibt es eh nicht, das ist mathematisch bewiesen.

    Da verweise ich dann aber auf Fachliteratur: http://de.wikipedia.org/wiki/Des_Kaisers_neue_Kleider

    Ok, willst du damit sagen, dass cooky nur die Augen vershcließt vor dem Offensichtlichen?
    Nämlich, dass auf jedenfall irgendwo etwas schiefgehen kann und damit alles zerschiest?


Anmelden zum Antworten