try catch in jeder funktion?



  • Soviel zum Thema "was ist ein Ausnahmefall?" 😉 Im Endeffekt hängt es doch wieder von der konkreten Anwendung ab, was du als sicher annehmen kannst und was nicht.

    Zum Beispiel: wenn ich mit einem CFileDialog den User nach einer Quelldatei frage, sichert der mir zu, daß diese Datei existiert - wenn dann das Öffnen dieser Datei scheitert, ist das unerwartet. (wenn du den Namen über cin eingegeben hast, ist es vermutlich deine Aufgabe, zunächst festzustellen, ob die Datei existiert)

    Außerdem hatte ich schonmal gesagt, daß nicht jeder Fehler zu einer Exception führen muß. Was ich sofort beheben kann, behebe ich auch sofort (Dateiname ungültig -> Rückkehr zum Anfang und anderen Namen erfragen). Erst wenn ich an der aktuellen Stelle keine Möglichkeit habe, etwas gegen den Fehler zu unternehmen (z.B. weil die Speicherroutine keine Möglichkeit hat, den User über Probleme zu informieren), werfe ich eine Exception - die dann von der Methode gefangen wird, die den Namen erfragt und das Speichern gestartet hat.



  • pumuckl schrieb:

    ...
    Nach allem, was ich bisher lesen durfte, ist alles, was einigermassen absehbar und abfangbar ist, mit normaler Fehlerbehandlung abzuhandeln und Ausnahmen eben nur ausnahmsweise zu benutzen.

    Hmmm - ist nicht ALLES absehbar, was man überprüft ?
    Wenn ich Speicher anfordere, weiß ich immer, dass er nicht reichen könnte (habe schonmal erlebt oder davon gelesen).
    Wenn ich nicht weiß, dass er nicht reichen könnte, werde ich dafür auch keinen Fehlermechanismus implementieren ...

    Ergo: Man braucht NIE exceptions. 😃

    Gruß,

    Simon2.



  • Simon2 schrieb:

    pumuckl schrieb:

    ...
    Nach allem, was ich bisher lesen durfte, ist alles, was einigermassen absehbar und abfangbar ist, mit normaler Fehlerbehandlung abzuhandeln und Ausnahmen eben nur ausnahmsweise zu benutzen.

    Hmmm - ist nicht ALLES absehbar, was man überprüft ?
    Wenn ich Speicher anfordere, weiß ich immer, dass er nicht reichen könnte (habe schonmal erlebt oder davon gelesen).
    Wenn ich nicht weiß, dass er nicht reichen könnte, werde ich dafür auch keinen Fehlermechanismus implementieren ...

    Ergo: Man braucht NIE exceptions. 😃

    dass hab ich mir nach pumuckls beitrag auch gedacht. eine exception wird ja nicht zufällig erzeugt, sondern, wenn eine variable für irgend eine operation einen ungültigen wert hat. und um sowas rauszufinden muss man irgendwas prüfen. das heißt nicht, dass ungültiger wert == exception, falls das einer flasch verstehen will. alles ist auf irgend einer ebene absehbar, und nur deshalb keine exception zu werfen ist falsch.



  • Simon2 schrieb:

    Bashar schrieb:

    ...
    Öhm, wäre das Ergebnis "kein Eintrag" nicht eine Art Fehlercode? Wo ziehst du denn da die Trennlinie?...

    Hier ist das einfach fachlich zu modellieren: Die leere Ergebnismenge ist erlaubt.
    std::string::length() gibt auch problemlos 0 zurück...

    Die leere Ergenismenge? Bei sucheEintrag hätte ich als Ergebnis einen Index erwartet, keine Menge. Das ist eine vollkommen andere Situation als bei der Länge eines Strings.



  • Simon2 schrieb:

    pumuckl schrieb:

    ...
    Nach allem, was ich bisher lesen durfte, ist alles, was einigermassen absehbar und abfangbar ist, mit normaler Fehlerbehandlung abzuhandeln und Ausnahmen eben nur ausnahmsweise zu benutzen.

    Hmmm - ist nicht ALLES absehbar, was man überprüft ?
    Wenn ich Speicher anfordere, weiß ich immer, dass er nicht reichen könnte (habe schonmal erlebt oder davon gelesen).
    Wenn ich nicht weiß, dass er nicht reichen könnte, werde ich dafür auch keinen Fehlermechanismus implementieren ...

    Ergo: Man braucht NIE exceptions. 😃

    Wenn du aber zu wissen glaubst, dass der Speicher auf jeden Fall immer ausreichen wird, es dir aber nicht leisten kannst, dich auf dieses Wissen zu verlassen, dann kommt die Ausnahme ins Spiel.



  • pumuckl schrieb:

    Wenn du aber zu wissen glaubst, dass der Speicher auf jeden Fall immer ausreichen wird, es dir aber nicht leisten kannst, dich auf dieses Wissen zu verlassen, dann kommt die Ausnahme ins Spiel.

    Und wenn ich zu wissen glaube, daß ich vom Nutzer den Dateinamen einer existierenden Datei übergeben bekommen habe, mir es aber nicht leisten kann, mich auf dieses Wissen zu verlassen? Die Situation sieht genauso aus 😉



  • Bashar schrieb:

    ...
    Die leere Ergenismenge? Bei sucheEintrag hätte ich als Ergebnis einen Index erwartet, keine Menge. Das ist eine vollkommen andere Situation als bei der Länge eines Strings.

    Von der Funktion habe ich nicht geredet, sondern von

    Simon2 schrieb:

    ...dass eine Funktion "sucheEintraege()" ...

    Bei einer "sucheEintrag()"-Funktion hätte ich noch mehr Anfragen an die fachlichen Anforderungen ...u.a.: "Was, wenn ich mehrere passende Einträge finde ?" (oder eben: keinen). Das kann ich so pauschal nicht beantworten, weil es einfach eine deutlich komplexere Anforderung ist.

    Gruß,

    Simon2.



  • Ups, wer lesen kann ... .)



  • pumuckl schrieb:

    ...Wenn du aber zu wissen glaubst, dass der Speicher auf jeden Fall immer ausreichen wird, es dir aber nicht leisten kannst, dich auf dieses Wissen zu verlassen, ...

    ... dann weißt Du aber, dass es DOCH Situationen geben kann, in denen er nicht reicht.

    Ich kann diese Regel höchstens als "Tendenz-Aussage" verstehen: "Je unwahrscheinlicher Dir die Situation erscheint, um so ernster solltest Du das Verwenden einer Exception in Erwägung ziehen." und "Je unwahrscheinlicher es Dir erscheint, um so ... Fehlercode ...".

    Leider ist das bei low-level- oder Querschnittsfunktionen Funktionen fast gar nicht mehr zu unterscheiden, weil ich gar nicht weiß, aus welcher Ausgangsposition ich aufgerufen werde ("Rechnet der Aufrufer vielleicht sogar damit, dass es keine Einträge gibt ?"...).

    Gruß,

    Simon2.



  • @pumuckl:

    Genau deshlab die strittigen Punkte: Die meisten Leute nehmen es als gesichert an, dass genug Speicher fuer ihr Programm zur Verfuegung steht. In dem Fall ist die Exception die richtige Antwort auf eine erfolglose Alloziierung. Wenn man aber weiss, dass der Speicher nicht unbedingt reichen muss, dann muss man das anders handhaben, z.B. indem man den User auffordert, andere Programme zu beenden.

    Nope, ganz böse falsch.

    Wenn man davon ausgeht dass der Speicher immer reichen muss, dann überschreibt (EDIT: redefiniert muss es denke ich richtig heissen) man new und macht ein "terminate()" statt dem "throw bad_alloc" rein! Dann ist das nämlich ein *Fehler* und keine *Ausnahme* mehr.

    Wenn man aber davon ausgeht dass der Speicher evtl. nicht reichen würde, dann verwendet man Exceptions.

    Der Fehler liegt darin dass du Fehler so behandelst wie man Ausnahmen behandeln sollte, und Ausnahmen so behandelst wie man den "Normalfall" behandeln sollte.



  • hustbaer schrieb:

    Wenn man davon ausgeht dass der Speicher immer reichen muss, dann überschreibt (EDIT: redefiniert muss es denke ich richtig heissen) man new und macht ein "terminate()" statt dem "throw bad_alloc" rein! Dann ist das nämlich ein *Fehler* und keine *Ausnahme* mehr.

    Man kann die bad_alloc-Ausnahme auch in der main fangen und das Programm terminieren.

    Und zwischen Fehler und Ausnahme kannst du sowieso nicht sinnvoll unterscheiden, da Fehler eine Ausnahme sein sollten und eine Ausnahme bedeutet, dass ein Fehler aufgetreten ist.



  • Badestrand schrieb:

    ...
    Man kann die bad_alloc-Ausnahme auch in der main fangen und das Programm terminieren....

    KANN man - aber es kann einem irgendwo zwischendrin mit catch(...) weggefangen werden. Das get mit terminate() nicht mehr.
    Außerdem hatte ich das so verstanden: "Was soll man lokal (also in der Funktion, die den Speicher anfordert) tun ?"

    Badestrand schrieb:

    ...
    Und zwischen Fehler und Ausnahme kannst du sowieso nicht sinnvoll unterscheiden, da Fehler eine Ausnahme sein sollten und eine Ausnahme bedeutet, dass ein Fehler aufgetreten ist.

    Es git auch eine Philosophie, die zwischen einem "(harten) Fehler" und einer "Ausnahmesituation" unterscheidet. Vertreter dieser Philosophie verwenden gerne terminate() und assert(). Ich selbst mache das nicht, aber ein exzellenter Externer (wirklich !!) machte das und auch andere Programmierer, die ich sehr schätze, tun es (andere wiederum nicht).

    Gruß,

    Simon2.



  • Um hier auch einmal meine (persönliche) Ansicht zu der Sache kund zu tun:

    Exceptions werfe ich immer dann, wenn eine Funktion (oder Methode) auf Grund Ihrer Vorbedingungen nicht das leisten kann, was von Ihr erwartet wird.

    Ich liebe Beispiele, von daher:

    unsigned int minusFour(unsigned int value);
    

    Gesetzt den Fall, es würde 3 übergeben werden, dann wäre das Ergebnis -1. Da aber ein vorzeichenloser Wert zurückgegeben werden soll, handelt es sich bei der Vorbedingung value=3 um eine Ausnahme, die (aufgrund der exzellenten Dokumentation der Funktion) nicht vorkommen sollte aber eben vorkommen kann.

    Da diese Funktion keine andere Möglichkeit besitzt, diese Situation mitzuteilen (von globalen Fehlerobjekten mal abgesehen), wird eben eine Exception verwendet:

    unsigned int minusFour(unsigned int value) throw(ValueError);
    

    Wenn eine Funktion einen Pointer zurück geben soll, dann hat man hier die Möglichkeit auf NULL zu prüfen, welches ein Wert ist, mit dem man durchaus weiterarbeiten kann. In dem Fall kann auf eine exception verzichtet werden.

    Grüße...

    Heiko



  • Simon2 schrieb:

    Badestrand schrieb:

    Man kann die bad_alloc-Ausnahme auch in der main fangen und das Programm terminieren....

    KANN man - aber es kann einem irgendwo zwischendrin mit catch(...) weggefangen werden. Das get mit terminate() nicht mehr.

    Ja, aber bad_alloc ist ja nur ein Fall. Außerdem müsste der Rest ja so ausgelegt sein, dass er mir nicht irgendwelche Ausnahmen wegfängt 🙂

    Simon2 schrieb:

    Außerdem hatte ich das so verstanden: "Was soll man lokal (also in der Funktion, die den Speicher anfordert) tun ?"

    Versteh ich nicht, da wirft man doch einfach die Exception? Und wenn man eine Funktion hat, die viel Speicher braucht, zur Not aber darauf verzichten könnte (es also nicht gleich zum Programm-Absturz kommen soll), kann man dort auf bad_alloc reagieren. Mit terminate geht das nicht :p



  • Badestrand schrieb:

    ...

    Simon2 schrieb:

    Außerdem hatte ich das so verstanden: "Was soll man lokal (also in der Funktion, die den Speicher anfordert) tun ?"

    Versteh ich nicht, da wirft man doch einfach die Exception? Und wenn man eine Funktion hat, die viel Speicher braucht, zur Not aber darauf verzichten könnte (es also nicht gleich zum Programm-Absturz kommen soll), kann man dort auf bad_alloc reagieren. Mit terminate geht das nicht :p

    Genau um die Frage, ob das das richtige Vorgehen ist, oder man im Fehlerfall lieber loggt und weitermacht oder terminate() aufruft oder ... dreht sich doch der ganze Thread (inzwischen).

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Genau um die Frage, ob das das richtige Vorgehen ist, oder man im Fehlerfall lieber loggt und weitermacht oder terminate() aufruft oder ... dreht sich doch der ganze Thread (inzwischen).

    Die Frage stellt sich imho nie wirklich. Wenn man durch einen Fehler in einen inkonsistenten oder ungültigen Zustand versetzt wird, in dem eine Behandlung nichts hilft ist auch Loggen alleine nicht mehr ausreichend. Bei allen Fehlern die Behandelt werden können, ist es eine Frage ob der Aufwand den sauber zu behandeln (sprich das Programm in einen gültigen Zustand zurückzuversetzen) in Projektspezifisch sinnvollen Rahmen möglich ist, oder eben nicht. In letzteren Fall reicht wieder mal ein Loggen nicht, in ersteren muss man aktiv werden und loggen kann als Ausgabe reichen.

    Zum bad_alloc habe ich irgendwann mal in einen Buch einen Tip gelesen: Reserviere am Programmstart einen Speicherbereich der groß genug ist für eine kurze Übergangszeit dem Programm das weiterlaufen ermöglichen zu können, und überschreibe new so, das wenn ein bad_alloc kommt zuerst der Speicherbereich freigegeben wird (+ Fehlermeldung "Warnung der Speicher geht zu neige, bitte Sichern sie alle Änderungen...") und erneut alloziert wird. Nur wenn der Bereich nicht mehr vorhanden ist und ein bad_alloc eintritt, ist das ein Abbruch der Anwendung wert.

    cu André



  • asc schrieb:

    ...
    Wenn man durch einen Fehler in einen inkonsistenten oder ungültigen Zustand versetzt wird, in dem eine Behandlung nichts hilft ...
    Bei allen Fehlern die Behandelt werden können, ...

    Das Problem ist wohl, dass man das im Einzelfall (lokal) nur sehr schwer unterscheiden kann.
    Wer das Glück hat, ein "Komplettsystem" zu erstellen, kann sich da noch eher etwas Konsistentes ausdenken als jemand, der nur irgendeine Querschnittsfunktion implementiert oder auf Vorgegebenes aufbauen muss.

    Gruß,

    Simon2.


Anmelden zum Antworten