C++0x checked exceptions?
-
Toni, es ging dem Fragesteller glaube ich nicht um die Ausnahme, sondern darum, wie man sie kommuniziert.
Und die Antwort lautet: In Projekten, in denen ich bisher mitgearbeitet habe, wurde das durch die Dokumentation erreicht.
-
Konrad Rudolph schrieb:
Toni, es ging dem Fragesteller glaube ich nicht um die Ausnahme, sondern darum, wie man sie kommuniziert.
Und die Antwort lautet: In Projekten, in denen ich bisher mitgearbeitet habe, wurde das durch die Dokumentation erreicht.
Genau sowas will ich wissen. Gibts noch andere Lösungen als in die Doku schreiben?
-
Theoretisch kannst du Exceptionspezifikationen (so heißen die glaub ich, man möge mich korrigieren wenn's nicht so ist) benutzen:
void do_some_risky_stuff () throw (risky_exception, stuff_exception);Das bewirkt nicht nur das der Anwender weiß „Aha, es fliegen möglicherweise folgende Exceptions“, es setzt sogar zwingend fest, dass /nur/ diese Exceptions geworfen werden dürfen.
Allerdings sind die böse, weil extrem fies zu implementieren. Einige Compiler ignorieren sie völlig, bei anderen bricht die Performance ein. In normalen C++-Programmen wird deshalb nur throw () verwendet, was garantiert, dass die Funktion keine Exception wirft.
Was man aber häufiger sieht ist etwas der Formvoid do_some_risky_stuff () // throw (risky_exception, stuff_exception);
-
.filmor schrieb:
es setzt sogar zwingend fest, dass /nur/ diese Exceptions geworfen werden dürfen.
Allerdings durch *Laufzeit*abfragen. D.h. es ist sehr wohl auch möglich, andere Ausnahmen zu schmeißen, allerdings wird dann 'unexpected' aufgerufen und das Programm beendet.
/EDIT: Details ausgelassen. Beispiel gibt's hier: http://cpptruths.blogspot.com/2007/05/use-of-stdbadexception.html
-
.filmor schrieb:
Das bewirkt nicht nur das der Anwender weiß „Aha, es fliegen möglicherweise folgende Exceptions“, es setzt sogar zwingend fest, dass /nur/ diese Exceptions geworfen werden dürfen.
Allerdings sind die böse, weil extrem fies zu implementieren.Die Implementation ist weniger wesentlich (zwar werden Exceptionspezifikationen heute gerne ignoriert, aber nur, weil sie sich bereits als nicht nützlich genug erwiesen haben). Das Problem ist tatsächlich die Semantik:
Eine Exceptionspezifikation bestimmt - wie richtig gesagt wurde - dass die betreffende Funktion nur diese Exceptions werfen darf. Sie bestimmt aber nicht, dass diese Funktion nur solche Exceptions werfen wird oder kann. Letzteres erlaubte es uns, Funktionen an kritischen Stellen einzusetzen, wo Fehlschläge nicht oder nur unter bestimmten definierten Bedingungen erlaubt sind; ersteres ist im Grunde nicht besonders nützlich: das ist nicht wesentlich anders als ein automatisches catch(A){throw;}catch(B){throw;}...{catch(...){terminate();} um jeden Funktionsaufruf.Nun kenne ich Java nicht - ich kann dem hier vorgebrachten Vorschlag aber nicht entnehmen, welche Semantik für checked exceptions gelten soll. Im Übrigen ist es auch kein Zufall, dass Exceptionspezifikationen diese weniger nützliche Semantik haben: etwas anderes ist schlicht technisch unmöglich. Wenn sich eine Funktionsimplementation darauf beschränken soll, dass nur andere Funktionen mit gleichermaßen oder stärker eingeschränkten Spezifikationen aufgerufen werden dürfen, so ist das zu einschränkend um nützlich zu sein: denn gerade den wichtigen Fall, dass wir eine Funktion schreiben, die von vornherein nicht für alle Teile erfolgreich sein muss, wird so nicht erfasst:
bool write_file(...) { try { write(); } catch(...) {return false;} return true;Diese Funktion könnte eine leere Exceptionspezifikation haben, für write ist das nicht der Fall. Erlaubt man aber den Aufruf von Funktionen mit weniger eingeschränkten Spezifikationen, so ist es im Allgemeinen (von catch(..) abgesehen) nicht beweisbar, dass die enthaltenen catch-Klauseln alle möglichen Fälle abdecken.
-
camper schrieb:
Die Implementation ist weniger wesentlich (zwar werden Exceptionspezifikationen heute gerne ignoriert, aber nur, weil sie sich bereits als nicht nützlich genug erwiesen haben).
Meine da mal was gelesen gehabt zu haben. Scheinbar schalten manche Compiler das Inlining bei solche Funktionen ab oder setzen es als
try { func(); } catch (erlaubte_exception& e) { throw; } catch (...) { unexpected (); }um.
Ich glaub es war dieser Artikel.
-
camper schrieb:
Nun kenne ich Java nicht - ich kann dem hier vorgebrachten Vorschlag aber nicht entnehmen, welche Semantik für checked exceptions gelten soll.
Eine Funktion in Java kann nur Checked Exceptions werfen die in der exception spezifikation stehen. das wird zur compiletime gecheckt.
in der theorie klingt das super, in der praxis eher nicht.
-
Das Problem welches sich bei Java gezeigt hat ist dass es ab und an mal vorkommt dass im Interface etwas vergessen wurde.
Wenn ich nun eine Funktion habe die laut Interface keine (checked) Exception werfen darf, und dann im nachhinein draufkomme dass eine konkrete Implementierung dieses Interface einfach nicht mit den aufgezwungenen Exception-Specs machen kann... doof.
Oder wenn ich die Implementierung dringend ändern will (weil sie z.B. viel zu langsam ist, oder Fehler enthält, oder was auch immer), dies aber nicht kann ohne in einer Funktion die ehemals als "ich werfe nix" gekennzeichnet war nun doch was zu werfen ... auch doof.
In dem Fall wirft man in Java dann einfach eine unchecked Exception ... was auch nicht Sinn der Sache ist.
Real sieht das dann so aus: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4615343
p.S.: da finden sich dann Kommentare wie
One can not change entries() to throw a ZipException -
it is too late.
One could parse entries in ZipFile constructor, and
throw a ZipException if the entry is invalidDanke, checked Exceptions

p.p.S.: in "eigenem" Code (auch wenn's ne kleine bis mittelgrosse Firma ist), sehe ich das Problem eher weniger. Einfach die Exception-Spec ändern, und so lange bei allen Projekten "compile drücken, Fehler durchgehen, Exception nachtragen oder fangen, compile drücken, ..." spielen bis es überall durchgezogen ist.
Im Framework Code, oder wenn's mal ne grössere Firma ist ... ist's halt etwas sehr doof.
-
Das hört man ja ziemlich oft "man kann nicht nachträglich eine Exception hinzufügen, dann compiliert der Code nicht mehr". Dieses Argument zehntausendmal zu bringen, macht es aber nicht richtiger und zeigt IMHO ganz genau auf, wie misverstanden checked Exceptions sind.
Wenn ich eine neue Exception werfe, dann breche ich Clientcode. Und zwar immer. Egal, ob sie checked ist oder nicht. Wenn sie nicht checked ist, führt das vielleicht nicht zu Compilerfehlern sondern zu Laufzeitfehlern, besser ist das aber keineswegs. Ich hatte vorher Code, der in allen möglichen Situationen robust war und es jetzt nicht mehr ist, möglicherweise sogar von mir unbemerkt. Da sind mir Compilerfehler lieber. Die Abwesenheit von Compilerfehlern bedeuted umgekehrt nicht, dass der Code noch funktioniert.
p.S.: da finden sich dann Kommentare wie Zitat:
One can not change entries() to throw a ZipException -
it is too late.
One could parse entries in ZipFile constructor, and
throw a ZipException if the entry is invalidDanke, checked Exceptions
Es ist nicht wegen checked Exceptions zu spät. Es ist zu spät, weil kein Clientcode mit dieser Exception rechnet.
Probleme mit checked Exceptions sehe ich eher im Aufwand während der Entwicklung. Wenn sich ständig was ändert, verliert irgendwann jeder im Team die Lust, die Exception-Spezifikationen überall anzupassen und wirft nur noch unchecked. Das ist verständlich und man bräuchte IMHO einen Weg, checked Exceptions zunächst einmal ignorieren zu können und vor dem Release dann zu aktivieren.
- Optimizer
-
Wenn ich eine neue Exception werfe, dann breche ich Clientcode. Und zwar immer.
Nö, der Clientcode kann ja die Exception in Klasse A einführen und in B fangen, aber dazwischen sitzt ein generisches Framework, das dank Checked Exceptions die Exception nichtmal einfach durchreichen kann. Danke aber auch.
-
Bashar hat es ja gesagt - aber ein weiteres Problem hier ist eben dass viele Leute denken dass der Exception Typ relevant ist. Das ist er aber nicht. Zumindest meistens. Ganz selten ist er mal interessant aber meistens fange ich einen Fehler. Was das für ein Fehler ist, ist mir meistens egal:
Fakt: ich konnte die Datei nicht öffnen.
Grund: entweder Datei nicht da, nicht genügend rechte, datei ist gelockt etc.Wichtig für mich ist: die Datei ist nicht offen und darauf muss ich reagieren.
Die grundlegende Idee ist ja nicht schlecht, aber in der praxis ist es einfach unpraktikabel.
-
Shade Of Mine schrieb:
Beruf: System Administrator
Musst du als System Admin soviel mit C++ programmieren oder machst du das privat?
-
Benutzerprofil schrieb:
Shade Of Mine schrieb:
Beruf: System Administrator
Musst du als System Admin soviel mit C++ programmieren oder machst du das privat?
C++-Sysadmins – kennste nicht? Das sind die Leute, die immer schnell ein C++-Skript mit Boost.Regex und Boost.Filesystem zusammenhacken, statt „grep“, „tr“ und „sed“ zu verwenden. :-p
(Shade weiß hoffentlich, dass das nicht ernst (oder gar bös) gemeint war.)
-
Benutzerprofil schrieb:
Shade Of Mine schrieb:
Beruf: System Administrator
Musst du als System Admin soviel mit C++ programmieren oder machst du das privat?
ich arbeite mehr mit c++ als mir lieb ist

-
Bashar schrieb:
Wenn ich eine neue Exception werfe, dann breche ich Clientcode. Und zwar immer.
Nö, der Clientcode kann ja die Exception in Klasse A einführen und in B fangen, aber dazwischen sitzt ein generisches Framework, das dank Checked Exceptions die Exception nichtmal einfach durchreichen kann. Danke aber auch.
Also hab ich nicht den direkt abhängigen Clientcode gebrochen, sondern den indirekt abhängigen. Wir verstecken den Fehler immer besser, nähern uns bald der Perfektion.

Shade Of Mine schrieb:
Bashar hat es ja gesagt - aber ein weiteres Problem hier ist eben dass viele Leute denken dass der Exception Typ relevant ist. Das ist er aber nicht. Zumindest meistens. Ganz selten ist er mal interessant aber meistens fange ich einen Fehler. Was das für ein Fehler ist, ist mir meistens egal:
Fakt: ich konnte die Datei nicht öffnen.
Grund: entweder Datei nicht da, nicht genügend rechte, datei ist gelockt etc.Wichtig für mich ist: die Datei ist nicht offen und darauf muss ich reagieren.
Die grundlegende Idee ist ja nicht schlecht, aber in der praxis ist es einfach unpraktikabel.
Wenn dich der Exception-Typ nicht interessiert, dann fang doch in deinem Clientcode die allgemeine Exception. Dann stört es dich auch nicht, wenn der andere seine Spezifikation etwas anpasst. Wenn du z.B. eine allgemeine IOException fängst weil es dir reicht zu wissen dass eine Datei nicht geladen wurde, dann ist es dir auch egal, wenn ich nachträglich noch eine AccesDeniedException hinzufüge.
-
Optimizer schrieb:
Wenn dich der Exception-Typ nicht interessiert, dann fang doch in deinem Clientcode die allgemeine Exception. Dann stört es dich auch nicht, wenn der andere seine Spezifikation etwas anpasst. Wenn du z.B. eine allgemeine IOException fängst weil es dir reicht zu wissen dass eine Datei nicht geladen wurde, dann ist es dir auch egal, wenn ich nachträglich noch eine AccesDeniedException hinzufüge.
Nicht desto trotz wiederspricht immer noch eine checked exceptions dem Policy-Design und einigen anderen Template-Techniken.
Wenn ich eine Templateklasse für Policies designe, KANN ich vorher garnicht wissen ob, und wenn welche Exceptions in Frage kommen können. Soll ich nun den Schreiber einer Policyklasse aufzwingen das er die Exception xyz verwenden muss?
Falls jemand das Prinzip von Policy-Klassen noch nicht verstanden hat soll er sich mal "Modern C++ Design" und die Loki-Bibliothek anschauen, letztere ist sehr stark darauf ausgelegt.
cu André
-
asc schrieb:
...sich mal "Modern C++ Design" und die Loki-Bibliothek anschauen, ...
Ach ja *schwärm* das steht auch noch auf meiner Wunschliste ...
Gruß,
Simon2.
-
Der Denkfehler ist, du müsstest jetzt wissen ob eine FileNotFoundException oder was auch immer kommen kann, aber das kann für jede Policy verschieden sein. Musst du aber nicht wissen. Wenn du Exception-Handling für deine Policies brauchst (wovon ich in den meisten Fällen nicht ausgehe), definierst du eine eigene Exception "kann dies und das nicht machen" und einigst dich darauf, dass alle Policies nur diese Exception werfen dürfen.
Das ist Standard-Vorgehen das du auch ohne checked Exceptions benötigst, wie könntest du sonst deinen Code absichern? Wenn du Policy-Objekte benutzt, hast du gewisse Annahmen darüber, wie sie sich verhalten und Exceptions gehören zu diesem Verhalten dazu. Wenn du keine Annahmen darüber machen kannst, kannst du deinen Code auch nicht absichern. Das Checking verursacht die Probleme also nicht, sondern zeigt die bereits vorhandenen nur auf.
Ich habe wie gesagt eher das Problem, dass sie zu unflexibel sind so lange man an dem Programm noch entwickelt und ständig was ändern muss. Ich sehe aber keinen Konflikt mit üblichen Paradigmen.
-
Optimizer schrieb:
Wenn du Exception-Handling für deine Policies brauchst (wovon ich in den meisten Fällen nicht ausgehe), definierst du eine eigene Exception "kann dies und das nicht machen" und einigst dich darauf, dass alle Policies nur diese Exception werfen dürfen.
Das ist u.U. eine zu starke Einschränkung.
Das ist Standard-Vorgehen das du auch ohne checked Exceptions benötigst, wie könntest du sonst deinen Code absichern?
Nein. Der Client weiß ja üblicherweise (zumindest irgendwo), welche Policy er verwendet, d.h. er weiß auch (irgendwo), welche Ausnahmen auftreten können und kann dort (= irgendwo) entsprechend reagieren. Das geht aber eben nur, wenn die Policy-parametrisierte Klasse diese Ausnahme überhaupt zulässt, und das ist eben durch Checked Exceptions stark beschnitten.
Wenn du Policy-Objekte benutzt, hast du gewisse Annahmen darüber, wie sie sich verhalten
Das ist eben nicht unbedingt der Fall. Zumindest nicht aus Sicht der allgemeinen, parametrisierten Klasse. Der Client macht natürlich Annahmen, aber der hat leider keinen Einfluss auf die von der parametrisierten Klasse erlaubten Ausnahmen.
=> Ich habe den Eindruck, dass Du hier die verschiedenen Akteure im Programmfluss durcheinanderbringst.
-
ihr stellt euch an. wenns Checked-Exceptions gäbe, warum sollte man dann den Exceptiontyp nicht per Template festlegen können? Fertig, Policy-Prinzip geht wieder.