Rückgabe: Fehlerbeschreibung



  • Mahlzeit,

    ich habe eine Methode, die eine Datei einliest und dabei können verschiedene Fehler auftreten. Ich muss dem Aufrufer also mitteilen ob was schief lief und was. Lösen wollte ich das so:

    bool Foo::methode(const std::string& file, float x, float y, std::string& error);
    

    Die Methode gibt false zurück wenn was schief lief und füllt dann error. Wäre das ok so? Das einzige was mich daran etwas stört ist, dass der Benutzer IMMER einen string übergeben muss. Der Aufruf sieht ja zwangsläufig immer so aus:

    string err;
    bool b = foo->methode("test.dat", 2, 3, err);
    if(!b) {
       mache was mit err
    }
    

    Wie könnte ich das elegant machen, dass der Benutzer nur optional einen string übergeben muss? (er zb keinen übergibt, wenn ihn der genaue Fehlertext nicht interessiert)



  • Was spricht denn gegen Exceptions?



  • Naja, zum einen finde ich Exceptions hässlich. Diese ganzen try { } catch Blöcke blähen den Code auf. Dann muss ich überall kommentieren welche Funktion welche Exceptions werfen kann usw.

    Außerdem dachte ich eine Exception ist für etwas, was eigentlich nicht auftreten dürfte. Quasi ein schwerer Fehler.



  • bool Foo::methode(const std::string& file, float x, float y, std::string* error=0)
    {
        // Code...
        if( error ) {
            *error = "Dieser oder jener Fehler ist geschehen...";
        }
    }
    }
    
    Foo f;
    std::string s;
    f.methode( "Nanana.txt", 12.0, 24.3, &s );
    std::cout << s << endl;
    // error interessiert nicht
    f.methode( "Nanana2.txt", 13.9, 11.18 );
    


  • knochentrocken schrieb:

    Naja, zum einen finde ich Exceptions hässlich. Diese ganzen try { } catch Blöcke blähen den Code auf. Dann muss ich überall kommentieren welche Funktion welche Exceptions werfen kann usw.

    ?!? Diese ganzen

    if( !f.methode() )
    { cout << "Fehler passiert: " << f.lastError() << endl; }
    

    Sind doch viel hässlicher. DIE blähen den Code auf und machen ihn auch noch unübersichtlich!
    Es ist viel schöner, wenn du zusammenhängende Logik ohne unnötige if/else in einem Block stehen hast und nachher in übersichtlicher Weise die Fehler abfängst, oder eben absichtlich durchpurzeln lässt, wenn er nicht umschiffbar ist, damit sich das Programm beendet.

    Außerdem werden Exceptions nicht irgendwo dokumentiert, die kommen mit in die Methoden Deklaration + Definition:

    void Foo::methode() throw( MyException );
    


  • knochentrocken schrieb:

    Außerdem dachte ich eine Exception ist für etwas, was eigentlich nicht auftreten dürfte. Quasi ein schwerer Fehler.

    Fehler, die nicht auftreten dürfen (Indexüberschreitung bei vector-Zugriff z.B.), fängt man normalerweise mit assert ab. Exceptions nimmt man bei Sachen, die durchaus passieren können, es im Regelfall aber nicht sollten. Es kann z.B. durchaus passieren, dass man keinen Speicher mehr allokieren kann oder dass eine Datei, die eigentlich vorhanden sein sollte, fehlt. Bei solchen "Ausnahme"-Fällen nimmt man idR Exceptions, new kann z.B. eine OutOfMemory-Exception werfen.



  • @dödelmeier: ich sehe da einen riesigen Unterschied. Wenn ich bei jedem Fehler eine Exception werfe, dann MUSS ich um JEDE Funktion, die potentielle eine Exception werfen kann (und das sind bei mir dann vieeeeele) ein try catch bauen.
    Wenn ich das nicht mache, zerlegt es mir bei einem Fehler das Programm. Ich finde sowas einfach potthässlich:

    try {
       foo.method();
    } 
    catch(Exception& e) {
      ...
    }
    ...
    try { foo2.method2();
    
    } 
    catch(Exception& e) {
     ...
    }
    

    Das finde ich viel übersichtlicher:

    if(  foo.method() ) {
      ...
    }
    ...
    foo2.method2(); // potentielle Fehler in method2() interessieren mich gerade nicht
    

    Wenn ich weiß das ein potentieller Fehler in einer Methode nicht so schlimm ist, kann ich auch einfach mal die if Abfrage lassen.

    Ich denke ich werd es mit einem string* Zeiger machen.



  • Wie könnte ich das elegant machen, dass der Benutzer nur optional einen string übergeben muss? (er zb keinen übergibt, wenn ihn der genaue Fehlertext nicht interessiert)

    Du koenntest die Funktion ueberladen. Blaeht aber das Interface auf. Alternativ kann die Fehlermeldung auch global abgelegt werden. In C gab es das System, dass man mit perror den letzten Fehlertext ausgeben konnte. So braucht man ueberhaupt keinen zusaetzlichen Parameter und hat auch Fehlerbehandlung von Schnittstelle getrennt.



  • Ähm, nö nicht um jede Funktion

    try {
        f.methode(); // wirft Exception1
        f.methode2(); // wirft Exception1
        someOtherDangerousThing(); // wirft Exception2
        std::vector<int> intVec;
        intVec.at(333); // wirft std::out_of_range
    } catch( const Exception1& e ) {
        // Behandlen
    } catch( const Exception2& e ) {
        // Behandeln
    } catch( const std::out_of_range& e ( {
        // Behandeln
    }
    

    Find ich angenehmer, da der Codefluss klar ist. Lass das mal komplexer sein als nur so paar Methodenaufrufe.

    Aber im Prinzip ist es Geschmackssache. Und wenn du ganz lieb zum User bist, gib dem Konstruktor deiner Objekte ein Flag mit, ob Methoden Exceptions werfen sollen oder über Rückgabewerte laufen 😉



  • Original Dödelmeier _P schrieb:

    Ähm, nö nicht um jede Funktion

    try {
        f.methode(); // wirft Exception1
        f.methode2(); // wirft Exception1
        someOtherDangerousThing(); // wirft Exception2
        std::vector<int> intVec;
        intVec.at(333); // wirft std::out_of_range
    } catch( const Exception1& e ) {
        // Behandlen
    } catch( const Exception2& e ) {
        // Behandeln
    } catch( const std::out_of_range& e ( {
        // Behandeln
    }
    

    Find ich angenehmer, da der Codefluss klar ist. Lass das mal komplexer sein als nur so paar Methodenaufrufe.

    Aber im Prinzip ist es Geschmackssache. Und wenn du ganz lieb zum User bist, gib dem Konstruktor deiner Objekte ein Flag mit, ob Methoden Exceptions werfen sollen oder über Rückgabewerte laufen 😉

    Ich finde, die Verwendung von Exceptions ist nicht Geschmacksache, sondern professionelles C++. Nicht umsonst kennen alle (oder die meisten?) modernen Sprachen Exceptions. Sieh dir doch nur das Problem an, mit dem dieser Thread eröffnet wurde! Das ist nur die Spitze des Eisbergs, wenn man mit Rückgabe-Codes arbeitet.

    Die Argumente für die Verwendung von Exceptions sind Legion und es gibt nur wenige ernstzunehmende Ausnahmen.

    Stefan.



  • knochentrocken schrieb:

    Naja, zum einen finde ich Exceptions hässlich. Diese ganzen try { } catch Blöcke blähen den Code auf. Dann muss ich überall kommentieren welche Funktion welche Exceptions werfen kann usw.

    Den Code muss es nicht aufblähen. Im Gegenteil es hilft den ganzen Code von Errorhandling zu befreien und macht das ganze klarer und auch kompakter.

    Du kannst einen Error Handler z.b in der main um alles machen und dann dort bestimmen, was passieren soll. (Meist ist das wohl eine Meldung in Form von Textausgabe).
    Wenn du dann etwas hast, wogegen du etwas tun kannst, dann bringst du einen Handler auf einer tieferen Stufe an. Um am Beispiel von Badestrand festzuhalten, könntest du, wenn du ein File nicht findest das abfangen und halt auf ein Dummy File zugreifen. (Falls das auch nicht klappt wird die Exception halt wieder gewofen).

    Außerdem werden Exceptions nicht irgendwo dokumentiert, die kommen mit in die Methoden Deklaration + Definition:

    Das wäre der Idealfall, wenn es so funktionieren würde, wie die meisten vermuten.. Allerdings verhalten sich Exception Spezifikationen nicht so, wie die meisten meinen:
    http://www.gotw.ca/publications/mill22.htm

    Daher ist es meist mit einem kurzen Kommentar eigentlich getan.


Anmelden zum Antworten