Warnungen mit Exceptions?



  • Wieso alles Reparieren? Kannst du das etwa?

    btw: 800x700 gibts nicht also nehmen wir 800x600 ist natürlich Blödsinn. Denn das bringt ja nix. Einfach defaultwerte nehmen weil wir lustig sind ist sinnlos. Lieber auf ne ordentliche Reaktion vom User der Engine warten.

    otze schrieb:

    assert ist für mich im code unfindbar, wenn ich auf fehlersuche bin, ein try/catch block fällt mir sofort ins auge.

    Hä?

    Mein assert sagt mir immer die Zeile, die Datei und die Funktion in der es ausgelöst wurde.
    OK, meine Exceptions liefern mir auch einen Stack Trace - aber das ist doch kein Argument gegen assert.

    das Failbit gefällt mir übrigens garnicht. denn immer wenn ein Fehler nur irgendwo gemeldet wird, aber nicht aktiv auftritt (wie bei einer exception) kann man ihn viel zu leicht ignorieren.



  • asserts muss man eigentlich überhaupt nicht suchen. Die schreibt man und vergißt man. Es sei denn, sie schlagen an.



  • @Shade: Man kanns ja auch ignorieren, dass die Engine den Fullscreen-Modus nicht geschafft hat, jeder weiterer Engine-Aufruf tut halt nichts mehr, das wird sehr schnell ein Disaster...

    MfG SideWinder



  • SideWinder schrieb:

    @Shade: Man kanns ja auch ignorieren, dass die Engine den Fullscreen-Modus nicht geschafft hat, jeder weiterer Engine-Aufruf tut halt nichts mehr, das wird sehr schnell ein Disaster...

    eben. Deshalb sind exceptions besser. Denn die kann man nicht so einfach ignorieren. Dafür muss man nämlich ein

    catch(...){}
    schreiben - das fällt auf.

    aber etwas das nicht da ist (Fehlerbehandlung) fällt nicht so leicht auf.



  • Dann habe ich aber wieder diese vielen catch-Blöcke, und imho entspricht es mehr der OOP-Logik, dass ein Objekt seinen Zustand verwaltet und nicht jemand anderen die Botschaft schickt "Hilfe, mach was!"

    Und auf Grund der Unübersichtlichkeit auf try/catch zu verzichten obwohl die Methode eine Exception werfen kann ist auch nicht das Gelbe vom Ei.

    Wenn die Größe natürlich zur CompileTime feststeht dann brauch ich nur beim ersten Mal try/catch.

    if(x!=800 && y !=600)
        throw;
    
    ...
    
    setres(800,600); // da brauch ich keinen try/catch mehr wenn ich weiß wann setres Exceptions wirft
    

    Aber ansonsten ist es eine grobe Fahrlässigkeit.

    Damit man diese Compiletime-Konstanten sofort auswerten kann gibts bereits ne Mailingliste dazu lang1@coollist.com , soll auf jeden Fall mit aufgenommen werden 😉

    [Das Plenken war Absicht...]

    MfG SideWinder



  • godlikebot schrieb:

    Shade of Mine schrieb:

    Eben nicht.
    Wieso sollte man soetwas schreiben?

    try
    {
        WasProbieren();
    }
    catch(...)
    {
        Reagieren();
    }
    
    try
    {
        WasAnderesProbieren();
    }
    catch(...)
    {
        AuchDaraufReagieren();
    }
    Und so weiter
    

    Kann man denn sagen, dass C-Style Fehlerbehandlung in C++ schlechterer Stil ist als Exceptions?

    Also mit Exceptions ist die Fehlerbehandlung IMHO erheblich einfacher (Mit entsprechenden Werkzeugen ➡ RAII / ScopeGuard ⚠ ). Normalerweise müsstest du eigentlich den Rückgabewert von praktisch jeder Funktion prüfen. Das ist a) lästig und b) ziemlich fehleranfällig, auch weil es in verschiedenen Bibliotheken jeweils anders gehandhabt wird (mal über Rückgabewert, mal errno, mal GetLastError). Da schreibe ich lieber meine Funktionen und werfe Exceptions. Der Aufrufer kann dann entscheiden, ob er es nochmal probieren will oder doch lieber den Vorgang abbricht und eine passende Fehlermeldung ausgibt.

    void BenutzerHatButtonGeklickt()
    {
    try
    {
        WasProbieren();
        WasAnderesProbieren();
    }
    catch(const std::exception& e)
    {
        HatLeiderNichtGeklapptWeil(e.what());
    }
    }
    


  • SideWinder schrieb:

    Dann habe ich aber wieder diese vielen catch-Blöcke, und imho entspricht es mehr der OOP-Logik, dass ein Objekt seinen Zustand verwaltet und nicht jemand anderen die Botschaft schickt "Hilfe, mach was!"

    Das Single-Responsibility-Principle steht dem entgegen. Ausserdem sind Exceptions sowieso für Fälle gedacht, in denen der Fehler nicht lokal behandelt werden kann. Das ist IMHO unabhängig von Stilfragen.



  • Aber du vergisst die Code-Übersichtlichkeit:

    // Ein...
    if(!setres())
        output_err(FATAL_ERROR,"Couldn't set resolution.",__FILE__,__LINE__);
    
    // ...ist sicherlich übersichtlicher als:
    try
    {
        setres();
    }
    catch(error& obj)
    {
        output_err(FATAL_ERROR,obj.what(),__FILE__,__LINE__-4);
    }
    

    Beinem vielleicht noch nicht, aber ab 10 untereinander wirds problematisch. Das obj.what() bringts halt noch zur detaillierteren Beschreibung des Fehlers (nun darf die Funktion ja dran - eine nicht gefundene Datei die zum Setzen der res benötigt wird, wird nicht plötzlich zu einem Grafikfehler...).

    MfG SideWinder



  • Warum hat man sowas eigentlich nicht eingeführt:

    // rückgabetyp name (params) : (exception_params)
    
    typ setres (int x, int y) : (const std::string&)
    {
        typ ret_data; // also rückgabewert benützen wir, keine status-meldung möglich
    
        if(x!=800 && x!=1024)
            warning("X is not a standard-value"); // string da std::string
    
        if(FAILED(Interface->SetScreenResolution(x,y)))
            error("can't set res"); // string da std::string
    
        return(ret_data);
    
        warning // Interne Reaktion
        {
             // Mach was bei Warnungen
             // Bevor das Schlüsselwort zählt impliziter aufruf von externer reaktion
             continue; // Weitermachen oben, andere Möglichkeit wäre throw
        }
    
        error
        {
             // Mach was bei Errors
             throw; // da fatal error
        }
    }
    

    Zudem kann der Aufrufer noch folgendes machen:

    void output_err (const std::string& bla) // da func ja std::string übernimmt
    {
        MessageBox(...);
    }
    
    void output_fatalerr (const std::string& bla) // dito
    {
        MessageBox(...);
        exit(-1);
    }
    
    func(1023,768) : warning(output_err), error(output_fatalerr);
    

    Oder so etwas in diese Richtung. Gibts da Probleme damit? Ansonsten schlag ich das für lang1 vor.

    MfG SideWinder



  • übersicht!

    du springst ja wirr herum.

    wie willst du auf ne warnung reagieren? entweder ist es ein fehler oder nicht. wenn es ein fehler ist, dann behandle oder wirf ihn. wenn es eine warnung ist, dann logge oder ignoriere sie.

    da brauch ich kein 'catch'...
    denn du hast im prinzip ein goto mit dynamischen labels gemacht.

    bzw: tut es hier eine funktion warning() vermutlich auch...



  • Ich weiß, dass ich da nur ein altes C-goto gekaspelt habe, aber ich finde den schlechten Stil nicht 😞

    Auf einen Fehler will ich schnell und an zentraler Stelle reagieren können um dann wieder mit meinem Code fortzufahren. Was ist daran so schlecht?

    MfG SideWinder



  • tag schrieb:

    Normalerweise müsstest du eigentlich den Rückgabewert von praktisch jeder Funktion prüfen. Das ist a) lästig und b) ziemlich fehleranfällig, auch weil es in verschiedenen Bibliotheken jeweils anders gehandhabt wird (mal über Rückgabewert, mal errno, mal GetLastError). Da schreibe ich lieber meine Funktionen und werfe Exceptions. Der Aufrufer kann dann entscheiden, ob er es nochmal probieren will oder doch lieber den Vorgang abbricht und eine passende Fehlermeldung ausgibt.

    Wenn ich aber nen dicken try Block mit vielen Befehlen hab und daraus ne exception fange, dann weiss ich doch nicht, wo das Programm stecken geblieben ist.
    Das ist ja das, was mich an exceptions etwas stört: Wenn eine exception geworfen wurde, dann ist das Programm (meistens) zum beenden verurteilt, oder seh ich das falsch? Der catch Block dient dann nur noch dazu, den Fehler zu loggen oder ne MessageBox auszugeben
    Die Lösung wäre eben um jede Funktion einen try/catch Block zu legen, was aber Spaghetti-Code ergibt und eigentlich wenig anders ist als das überprüfen der Rückgabewerte der Funktionen.



  • Man muss nach der goldenen Mitte suchen 🙂

    Beispiel:

    void OnSaveButtonClicked()
    {
    try
    {
    ofstream(blabla);
    speichere_datei();
    }
    catch(std::exception&)
    {
    MessageBox("Folgender Fehler ist beim Speichern aufgetreten: ...");
    }
    }
    

    Wenn du in diesem Fall das Programm einfach abbrichst, dann bin ich der erste, der dir in den Hintern tritt 😃
    Oder wenn du eine etwas kompliziertere Rechnung startest und dir auf einmal der Speicher ausgeht, warum dann nicht einfach das unvollständige Ergebnis verwerfen, den Ausgangszustand wiederherstellen und dem Benutzer sagen, was nicht geklappt hat? Oder wenn du eine Datei über's Netzwerk verschickst, warum sollte dein Programm abstürzen, wenn der Zielcomputer sich verabschiedet hat? In allen diesen Fällen ist ein normales Weiterarbeiten meistens problemlos möglich.
    Wenn du aber z.B. nicht mal mehr genug Speicher hast, um ein Meldungsfenster anzuzeigen, dann lässt es sich nur noch schwer weitermachen...
    Exceptions sind ja nur ein anderer Weg für Fehlerbehandlung. Wenn es mit klassischen Rückgabewerten keinen Grund gibt, das Programm zu beenden, dann gibt es den mit Exceptions auch nicht. Der (meines Erachtens) große Vorteil von Exceptions ist aber, dass einem die Fehlerbehandlung erleichtert wird. Es gibt eine einheitliche Fehlerbehandlung und ich kann mich eher damit auseinandersetzen, was im Fehlerfall zu tun ist, als den Fehlerfall überhaupt erstmal festzustellen.



  • gibts denn noch irgendwas, was man machen kann, wenn new fehlschlägt?(ausser exit)



  • gibt's denn noch irgendwas, was man machen kann, wenn malloc fehlschlägt?

    Sofern es sich nicht um wirklich kritische Daten handelt (Erstellen eines Threads, Anlegen von Synchronisationsobjekten, Laden einer DLL etc.): Die aktuelle Aktion abbrechen, alle bisherigen Änderungen zurücknehmen und versuchen zu retten, was noch zu retten ist.

    Das hat aber alles nicht wirklich etwas mit Exceptions zu tun; alle diese Probleme hat man auch ohne.

    Ich weigere mich zu glauben, dass hier eine Person mitliest, die knallhart mitten in den Tiefen eines Programms

    void* data = malloc(size);
    if(!data)
    exit(1);
    

    schreiben würde.

    T* data = new T; // und wenn's nicht geht, dann wird halt terminate() aufgerufen, na und?
    

    scheint sich ja recht großer Akzeptanz zu erfreuen.



  • godlikebot schrieb:

    Wenn ich aber nen dicken try Block mit vielen Befehlen hab und daraus ne exception fange, dann weiss ich doch nicht, wo das Programm stecken geblieben ist.
    Das ist ja das, was mich an exceptions etwas stört: Wenn eine exception geworfen wurde, dann ist das Programm (meistens) zum beenden verurteilt, oder seh ich das falsch?

    Richtig, das siehst du falsch.

    Eine Exception ist ein Fehler.
    Normalerweise baut ein Programm aber auf dem funktionieren der einzelnen Teile auf.

    Wenn also jetzt mittem im Rendern einer Szene eine 'Device Lost' Exception fliegt, kann man versuchen das Device wieder herzustellen. Dabei ist es unerheblich ob das Device Lost beim Init, Draw oder Flip aufgetreten ist. Du behandelst es immer gleich: versuchen Device wiederherzustellen.

    Du hast Exception nur nicht verstanden 😉 Lies mal die GotW - da gibts einiges interessantes zu Exception.

    Der catch Block dient dann nur noch dazu, den Fehler zu loggen oder ne MessageBox auszugeben

    Manchmal schon. Denn manchmal kann man auf einen Fehler nicht reagieren, weil er kritisch ist. zB "File Not Found" wenn der User ne Datei öffnen will. Da muss man dann den User fragen was er stattdessen machen will.

    Die Lösung wäre eben um jede Funktion einen try/catch Block zu legen, was aber Spaghetti-Code ergibt und eigentlich wenig anders ist als das überprüfen der Rückgabewerte der Funktionen.

    Es ist aber unerheblich _welche_ Funktion fehlschlägt.

    Es ist dir doch eigentlich egal, ob du ein "Printer Not Ready" beim
    Printer printer(LPT1);
    oder beim
    printer<<"Hallo";
    oder beim
    printer.setWidth(7);
    bekommst.

    Oder gar ganz woanders.

    Es ist unwichtig. Du reagierst ja nicht darauf, dass "printer<<"Hallo";" nicht funktioniert, sondern darauf, dass "Printer Not Ready" passiert ist.



  • OK, so langsam fang ich an, exceptions zu verstehen. Danke erstmal für die Erklärungen.

    Shade Of Mine schrieb:

    Du hast Exception nur nicht verstanden Lies mal die GotW - da gibts einiges interessantes zu Exception.

    Und was sind die GotW? 🙂



  • godlikebot schrieb:

    Und was sind die GotW? 🙂

    schonmal gegooglet 😉

    www.gotw.ca/gotw

    GotW (guru of the week) sind 'Artikeln' von Herb Sutter. Er stellt ne Frage, und beantwortet sie dann. Sehr interessant.

    Seine beiden Bücher (Mehr) Exceptional C++ basieren recht stark auf den GotW



  • Super! Danke! 🙂


Anmelden zum Antworten