Exception Frage
-
Ausgraber

~john schrieb:
So bleibt im Grunde als einziger Grund throw() nicht zu verwenden, schlechte Compiler die nicht optimieren können.
Neben der Verwaschung relevanter Informationen. Ich fände es viel wichtiger, exception-werfende Funktionen speziell kennzuzeichnen, als nicht werfende. Denn diese sind nämlich die Ausnahme (schon per Definition) und vor allem der Teil, der besondere Aufmerksamkeit vom Benutzer erfordert. Es gibt zum Beispiel Frameworks, die als bewusste Designentscheidung auf Exceptions ganz verzichten (und damit meine ich nicht antike wie wxWidgets, welche vor modernem C++ entstanden sind). Welchen Sinn hätte es, wenn dort jede Funktion ein
throw()besässe?Man muss vielleicht auch zwei Kontexte unterscheiden: Zum einen Funktionen auf hoher Abstraktionsebene, z.B. von Framework-Schnittstellen, zum anderen elementare Low-Level-Operationen wie Non-Throwing Swap. Bei ersteren kann oft nicht garantiert werden, dass keine Exception fliegt. Es könnte ja irgendeine Kopie fehlschlagen, weil der Speicher ausgeht. Halt Fälle, die nie auftreten, aber alles lahm legen, falls sie doch auftreten. Auf dieser Ebene macht es meiner Meinung nach Sinn, "beabsichtigte" Exceptions zu dokumentieren, ansonsten nichts.
Beim Low-Level-Fall finde ich es hingegen angebracht,
throw()– oder vielleicht besser einen Dokumentationshinweis – hinzuschreiben, falls wirklich unter keinen Umständen eine Exception fliegen kann. Schliesslich muss man hier möglichst generisch bleiben und dafür sorgen, dass grundlegende Dinge wie Speicherfreigabe noch funktionieren. Also Gegebenheiten, auf die sich der Benutzer 100% verlassen kann.
-
Nexus schrieb:
Ich fände es viel wichtiger, exception-werfende Funktionen speziell kennzuzeichnen, als nicht werfende. Denn diese sind nämlich die Ausnahme (schon per Definition) und vor allem der Teil, der besondere Aufmerksamkeit vom Benutzer erfordert.
Das ist bei mir aber anders. Fast jede Funktion benutzt Freispeicher oder was grafisches oder was mit Dateien oder irgendwelche anderen Ressourcen, die kurz mal nicht da sein könnten. Entweder direkt oder indem sie Funktionen aufruft, die das Problem haben.
Nexus schrieb:
Es gibt zum Beispiel Frameworks, die als bewusste Designentscheidung auf Exceptions ganz verzichten (und damit meine ich nicht antike wie wxWidgets, welche vor modernem C++ entstanden sind).
Diese Frameworks sind ein einziger Designfehler? Weiß nicht, aber es fühlt sich unglaublich komisch an. Werden alle Probleme wie damals per return hochgereicht?
Nexus schrieb:
Man muss vielleicht auch zwei Kontexte unterscheiden: Zum einen Funktionen auf hoher Abstraktionsebene, z.B. von Framework-Schnittstellen, zum anderen elementare Low-Level-Operationen wie Non-Throwing Swap. Bei ersteren kann oft nicht garantiert werden, dass keine Exception fliegt. Es könnte ja irgendeine Kopie fehlschlagen, weil der Speicher ausgeht. Halt Fälle, die nie auftreten, aber alles lahm legen, falls sie doch auftreten. Auf dieser Ebene macht es meiner Meinung nach Sinn, "beabsichtigte" Exceptions zu dokumentieren, ansonsten nichts.
Uih, da bin ich nicht so sicher. Ja, es mag so erscheinen, als müsse man sich am besten fesseln, damit man keine Fehler mehr macht. Aber ist das nicht neulich mit Java doll in die Hose gegangen? Das Framework steht zwischen den OS-Wrappern und der Anwendungslogik und die Anwendungslogik muß auf Exceptions des OS reagieren.
Nexus schrieb:
Beim Low-Level-Fall finde ich es hingegen angebracht,
throw()– oder vielleicht besser einen Dokumentationshinweis – hinzuschreiben, falls wirklich unter keinen Umständen eine Exception fliegen kann. Schliesslich muss man hier möglichst generisch bleiben und dafür sorgen, dass grundlegende Dinge wie Speicherfreigabe noch funktionieren. Also Gegebenheiten, auf die sich der Benutzer 100% verlassen kann.Jup.
Viele der elementaren Funktionen sind Templates und können deswegen nichts sagen. Warum eigentlich nicht? Wenn schon Exceptionspezifikationen, mußte das eine Compilersache sein und keinen Laufzeitfehler erzeugen. Dazu müßten sie per Template-Meta-Magie vom Benutzer erzeugt werden können. Und selbst dann wäre es nichts, was ich mögen würde, wegen zu viel Aufwand ohne erkennbaren Sinn, dem Anzeichen einer Softwareseifenblase (Ich erinnere an die Ungarische Notation, Struktogramme, Strukturierte Programmierung und UML).
Ok, ein nothrow verstehe ich sehr gut, insbesondere wird es lecker, wenn man das zur Compilezeit abfragen kann und anhand dessen seinen Code anpassen kann. Ich spüre des öfteren das Verlangen, etwas Einfaches einfach zu lassen und nicht (wegen der Annahme, daß eine Zuweisung evtl nicht klappen könnte) exceptionsicher zu machen.
//soll entweder von begin bis end alles füllen oder gar nichts void fill(T const& value,T* begin,T* end) { queue<T> tmp; for(T* pos=begin;pos!=end;++pos) tmp.push_back(value); #ab_jetzt_nothrow for(T* pos=begin;pos!=end;++pos) { swap(*pos,tmp.peek_front(); tmp.pop_front(); } }//soll entweder von begin bis end alles füllen oder gar nichts //wenn ich doch nur wüßte, daß der op=(T,T) nothrow ist... *träum* void fill(T const& value,T* begin,T* end) { while(begin!=end) *begin=value; }
-
volkard schrieb:
Das ist bei mir aber anders. Fast jede Funktion benutzt Freispeicher oder was grafisches oder was mit Dateien oder irgendwelche anderen Ressourcen, die kurz mal nicht da sein könnten. Entweder direkt oder indem sie Funktionen aufruft, die das Problem haben.
Ja, Freispeicher benutze ich auch oft. Meistens zwar nicht direkt, aber in Klassen, die sich Speicher holen. Aber eben: Auf hoher Abstraktionsebene kann dauernd etwas schiefgehen. Ich bin mir nicht sicher, ob sich ~john auch für
throw()ausspricht, wenn keine beabsichtigten Exceptions fliegen können. Ich denke nicht (d.h.throw()nur wenn wirklich rein gar nichts fliegt), dann stehtthrow()wieder nur an sehr wenigen Orten und ich bin wieder gleicher Meinung. Sorry, wenn ich da was durcheinandergebracht habe.Was ich damit sagen will:
/// @throw FileNotFound Datei konnte nicht gefunden werden void LoadData(const char* fileName);ist für mich viel wertvoller als
/// @throw std::bad_alloc Kein Speicher für Eintrag void AddEntry(const Entry& entry)Denn wie will man auf das zweite sinnvoll reagieren? Jeder Versuch könnte seinerseits wieder Probleme verursachen. Abgesehen davon müsste man einen sehr grossen Teil der Funktionen mit so einer Information zupflastern.
volkard schrieb:
Diese Frameworks sind ein einziger Designfehler? Weiß nicht, aber es fühlt sich unglaublich komisch an. Werden alle Probleme wie damals per return hochgereicht?
Designfehler würde ich nicht sagen. Zum Beispiel scheint mir das Multimedia-Framework SFML sehr gut durchdacht und in modernem C++ geschrieben zu sein, aber es verwendet keine Exceptions. Fehlerquellen gibts hauptsächlich beim Laden von Ressourcen (Bilder, Schriftarten etc.). Da hat der Entwickler sich entschieden,
bool-Rückgaben zu verwenden. Man kann sich darüber streiten, ob es besser ist. Soweit ich weiss, war einer der Gründe, dass man mit dem Programm dennoch weiterarbeiten kann. Also dass statt eines Bilds ein weisses Rechteck angezeigt wird. Ich kann mir durchaus vorstellen, dass so eine Semantik erwünscht sein kann.An den Stellen, wo ich auf korrekt geladene Ressourcen angewiesen bin, habe ich mir Wrapper geschrieben, die Exceptions werfen können. So muss ich trotzdem nicht C-Style-Hochreichen betreiben.
volkard schrieb:
Uih, da bin ich nicht so sicher. Ja, es mag so erscheinen, als müsse man sich am besten fesseln, damit man keine Fehler mehr macht. Aber ist das nicht neulich mit Java doll in die Hose gegangen? Das Framework steht zwischen den OS-Wrappern und der Anwendungslogik und die Anwendungslogik muß auf Exceptions reagieren.
Ich bin mir nicht sicher, was du meinst. Ich spreche mich jedenfalls nicht für Checked Exceptions wie in Java aus.

Um nochmals meine Ansicht zusammenzufassen:
- Exception-Dokumentation bei Highlevel-Exceptions wie
FileNotFound. - Keine Angabe bei Funktionen, die nicht zum Werfen von Exceptions beabsichtigt sind (im Beispiel
AddEntry()). throw(),noexceptoder Dokumentation bei generischen Low-Level-Funktionen, die für Exceptionsicherheit essentiell sind.
- Exception-Dokumentation bei Highlevel-Exceptions wie
-
volkard schrieb:
//soll entweder von begin bis end alles füllen oder gar nichts void fill(T const& value,T* begin,T* end) { queue<T> tmp; for(T* pos=begin;pos!=end;++pos) tmp.push_back(value); #ab_jetzt_nothrow for(T* pos=begin;pos!=end;++pos) { swap(*pos,tmp.peek_front(); tmp.pop_front(); } }1. Python Kommentare funktionieren nicht in C++.
2. Das normal swap ist nicht non-throw http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-throwing_swap