C++ Exceptions und Relevanz



  • sebi707 schrieb:

    Andromeda schrieb:

    Wie setjmp/longjmp, die gibt es schon seit den C-Tagen.

    Meiner Meinung nach ist setjmp/longjmp eins der schrecklichsten C-Features überhaupt... (libpng jemand?).

    Libpng nicht, aber IJG-jpeg, bei der es zwischen meinen verwendeten Bibliotheken einen Versionskonflikt gab (Qt hatte seine eigene Version mitgebracht und einkompiliert). Fehler bei Initialisierung wurde nicht bedacht (ohne setjmp), dann der longjmp ins Nirvana zurück. Schon lustig zu debuggen wenn der irgendwo in den tiefen des Programms in einem der knapp 40 Threads einen Nullpointer dereferenziert und ihm dabei der Stack-Pointer fehlt... kein Call Stack... Debugger sagt: WTF? Keine Ahnung wo das grad passiert ist. Viel Spass beim Raten 😃

    Finnegan



  • Arcoth schrieb:

    A language that doesn't affect the way you think about programming is not worth knowing.

    Quatsch.

    Und was ist daran Quatsch? Mal davon abgesehen dass "worth" prinzipiell etwas Subjektives ist.

    Weißt du eigentlich was QFT bedeutet?

    @hustbaer:

    Weil man höllisch aufpassen muss. Ein zu aggresives catch irgendwo killt dir den ganzen Mechanismus und ist quasi nicht auffindbar.

    Prinzipiell ist es ein goto und das hat eben gewisse Nachteile.


  • Mod

    Shade Of Mine schrieb:

    Und was ist daran Quatsch?

    Sprachen sind Werkzeuge. Dein Zitat sagt sich schön, aber ich kann nicht erkennen, warum die genannte Bedingung hinreichend sein soll.

    Weißt du eigentlich was QFT bedeutet?

    Ja



  • Shade Of Mine schrieb:

    Weil man höllisch aufpassen muss. Ein zu aggresives catch irgendwo killt dir den ganzen Mechanismus und ist quasi nicht auffindbar.

    Das geht aber nur mit einem catch(...) (der Typ des geworfenen Objekts ist schließlich weggekapselt und nicht sichtbar). Und wenn man nach einem catch(...) nicht re-throwt ist man ja wohl selbst schuld bzw. hat eh ganz andere Probleme.

    Shade Of Mine schrieb:

    Prinzipiell ist es ein goto und das hat eben gewisse Nachteile.

    Ich finde es ist eigentlich eher ein allgemeineres return , wobei das geworfene Objekt der Rückgabewert ist.



  • Weil es gerade zum Thema passt, ein interessanter Vortrag von Andrei Alexandrescu zum Thema Fehlerbehandlung in C++, in dem er das Expected<T> -Idiom vorstellt (wer's noch nicht kennt):

    https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C

    Ich habe Expected<T> zwar noch nicht verwendet, auf den ersten Blick finde ich es jedoch sehr anprechend, da es offenbar nahezu ohne Overhead die Vorteile von Fehlerbehandlung via Rückgabewert und Exceptions in sich vereint.

    Gruss,
    Finnegan





  • expected<useless> schrieb:

    Wers noch nicht kennt: http://en.cppreference.com/w/cpp/experimental/optional

    Wenn du den Vortrag tatsächlich gesehen und verstanden hättest, würdest du wissen, dass er dort auch auf boost::optional eingeht (was de facto dasselbe ist) und wüsstest, was der Unterschied zu Expected<T> ist.

    Einfach ausgedrückt: wenn du irgendwo in deinem Code, fernab der Funktion, die den Wert gesetzt hat einen optional<T> in die Hand gedrückt bekommst und dieser keinen "value" hat, obwohl er einen haben müsste, woher weisst du dann was schiefgelaufen ist?

    Und wenn du ein Kopzept schon durch deine Namenswahl implizit als nutzlos bezeichnest, dann kann ich das nur dann akzeptieren, wenn du das auch zumindest ansatzweise begründest (lasse mich immer gerne überzeugen wenn es einleuchtet). Behauptungen aufstellen und einen sinnlosen Link posten ist da leider etwas zu wenig.

    Finnegan



  • happystudent schrieb:

    Shade Of Mine schrieb:

    Weil man höllisch aufpassen muss. Ein zu aggresives catch irgendwo killt dir den ganzen Mechanismus und ist quasi nicht auffindbar.

    Das geht aber nur mit einem catch(...) (der Typ des geworfenen Objekts ist schließlich weggekapselt und nicht sichtbar). Und wenn man nach einem catch(...) nicht re-throwt ist man ja wohl selbst schuld bzw. hat eh ganz andere Probleme.

    Achnene, da hat Shade schon Recht.
    catch (...) ohne re-throw ist das was man manchmal einfach braucht.
    Dummerweise.
    Wenn die Welt schön rosa wäre, und alle "normalen" Exception-Klassen von std::exception abgeleitet wären, dann wäre es 'was anderes. Ist aber leider nicht so. Es gibt immer noch tonnenweise Libraries die eigene Exception-Klassen verwenden die nicht von std::exception abgeleitet sind.
    Weil die Library entwickelt wurde als C++ noch nicht ordentlich standardisiert war, weil die Library von jemandem entwickelt wurde der sich nicht im Klaren darüber war was er damit anrichtet, weil von solchen Libraries "abgeschrieben" wurde ...

    Und es gibt dummerweise auch Stellen wo man ein "egal was schiefgegangen ist, hier stellen wir jetzt fest dass wir nicht machen konnten was wir machen wollten, und machen dann mit dem restlichen Programm weiter" braucht.
    An diesen Stellen dann jeweils 5+ catch Blöcke zu schreiben ist einfach nicht praktikabel. Und im Endeffekt hat man dann eine Exception-Klasse übersehen, und ist erst wieder angeschmiert.

    BTW: Den Typ des geworfenen Objekts kann man sich mittels re-throw in einer geeigneten Hilfsfunktion, die dann die dutzenden verschiedenen catch-Blöcke enthält, die man sonst an 1000 Stellen schreiben müsste, besorgen.
    Ich hab' da z.B. eine GetErrorMessageFromCurrentException() und eine LogCurrentException() Hilfsfunktion.
    Und eine Guard-Klasse die sich um so gottlose Drecksklassen wie CException (MFC) kümmert -- damit am Ende des catch-Handlers dann auch e->Delete() aufgerufen wird.
    Exception beliebigen Typs fangen + loggen + ggf. entsorgen ( CException ) beschränkt sich damit auf

    //...
    catch (...)
    {
        CleanupExceptionGuard guard;
        LogCurrentException();
    }
    

    ps: Und catch (...) mit re-throw ist ja nun wirklich bäh. Dafür baut man sich passende Hilfsklassen. Im Falle des Falles, also wenn es in einem Programm wichtig ist, kann man dann nämlich Code-Teile ohne äusseres catch laufen lassen, und die darin auftretenden Exceptions die niemand fangen mag von einem Crash-Handler verarbeiten. Der kann dann einen Dump schreiben wo das Programm genau in dem Zustand ist wo die Exception geworfen wurde. Nicht 10 Levels tiefer im Callstack wo ein blödes catch (...) mit re-throw steht, und man schon keinen Anhaltspunkt mehr hat wo die Exception überhaupt entstanden ist.
    In Anwendungscode mag das jeder halten wie er es für richtig hält. In Libraries hat ein catch (...) mit re-throw meiner Meinung nach aber genau nichts verloren.



  • hustbaer schrieb:

    BTW: Den Typ des geworfenen Objekts kann man sich mittels re-throw in einer geeigneten Hilfsfunktion, die dann die dutzenden verschiedenen catch-Blöcke enthält, die man sonst an 1000 Stellen schreiben müsste, besorgen.

    In der Präsentation von Andrei Alexandrescu (Folie 26) gibt es ein lustiges Konstrukt:

    template<class E>
    bool hasException() const {
      try {
        if(!gotHam) std::rethrow_exception(spam);
      } catch(const E& object) {
        return true;
      } catch (...) {
      }
      return false;
    }
    

    Darauf wäre ich wohl nicht so schnell gekommen...



  • hustbaer schrieb:

    Achnene, da hat Shade schon Recht.
    catch (...) ohne re-throw ist das was man manchmal einfach braucht.
    Dummerweise.

    Ja, ist halt dann ein hack mit dem man sich jede Menge anderer Probleme einhandeln kann.

    Weiß nicht, so ganz überzeugt mich diese Argumentation noch nicht:

    hustbaer schrieb:

    An diesen Stellen dann jeweils 5+ catch Blöcke zu schreiben ist einfach nicht praktikabel. Und im Endeffekt hat man dann eine Exception-Klasse übersehen, und ist erst wieder angeschmiert.

    Muss man ja auch nicht? Vor allem wenn man sich eh schon die Mühe macht:

    hustbaer schrieb:

    BTW: Den Typ des geworfenen Objekts kann man sich mittels re-throw in einer geeigneten Hilfsfunktion, die dann die dutzenden verschiedenen catch-Blöcke enthält, die man sonst an 1000 Stellen schreiben müsste, besorgen.

    Schreibt man sich halt eine kleine Hilfsfunktion die die Exceptions entsprechend um-wrappt. Dann muss man nicht "an 1000 Stellen" die catches schreiben, sondern genau einmal und hat danach eine saubere Möglichkeit mit der Library zu arbeiten?

    template <typename CodeSegment>
    void wrap_ugyl_library(CodeSegment cs)
    {
        try { cs(); }
        catch (exception_1 const &) { /* wrap ugly exceptions here */ }
        catch (int) {}
        catch (std::string) {}
        // ... etc.
    }
    
    // ...
    
    void run_impl()
    {
        ugly_library_function_1();
        ugly_library_function_2();
        ugly_library_function_3();
    }
    
    void run()
    {
        try
        {
            wrap_ugyl_library([&]
            {
                run_impl();
            });
        }
        catch (std::exception const &wrapped_exception)
        {
            std::cerr << wrapped_exception.what() << '\n';
        }
    }
    


  • @happystudent
    Du siehst den Code ernsthaft als Alternative? Ich nicht, sorry.
    Dafür ist die Lambda-Syntax zu hässlich.
    Programmierst du so, oder ist das einfach nur Apologetik?

    Und an den vielen Programme die vor 2011 entwickelt wurden (bzw. wo die Entwicklung vor 2011 begonnen hat), und die noch jahrelang weiter entwickelt/supportet werden müssen ändert es auch nix.



  • hustbaer schrieb:

    @happystudent
    Du siehst den Code ernsthaft als Alternative? Ich nicht, sorry.
    Dafür ist die Lambda-Syntax zu hässlich.
    Programmierst du so, oder ist das einfach nur Apologetik?

    Hm, ich find die Lambda Syntax eigentlich sehr gut muss ich sagen. Also ja, ich programmiere so 😃

    Bietet sich ja auch in der std-lib oft genug an:

    std::sort(vec.begin(), vec.end(), [](my_obj const &left, my_obj const &right)
    {
        return left.value < right.value;
    });
    

    Ist ja quasi das selbe, dachte genau für solche Geschichten sind Lambdas da. Man kann das ja auch anders schreiben:

    execute_safe([&]{ func(); }); // bzw. ohne captures:
    execute_safe(func);
    

    Find jetzt nicht dass das schlecht aussieht. Ich mag die Syntax, hab aber den Fall auch gar nicht weil ich (bis jetzt) noch keine libs verwendet habe die mit unbekannten Objekten um sich throw-en.

    Das Problem könnte man ja durch die Einführung einer Art std::auto_rethrow_exception lösen, damit könnte man dann die C# ThreadAbortException imitieren.

    Aber ich geb zu dass die Methode nicht 100% Wasserdicht ist.



  • happystudent schrieb:

    Hm, ich find die Lambda Syntax eigentlich sehr gut muss ich sagen. Also ja, ich programmiere so 😃

    Du schreibst genau solche "mach mal X innerhalb eines try-catch" Funktionen? Hm. Naja, gut, sicherlich auch viel Gewohnheitssache. In C# mach ich ähnliche Sachen ja auch. Vielleicht würde ich mich schnell daran gewöhnen wenn ich einen C++ Compiler hätte der nicht Jahre bevor C++11 rauskam schon sein letztes Service Pack bekommen hat.

    happystudent schrieb:

    Ich mag die Syntax, hab aber den Fall auch gar nicht weil ich (bis jetzt) noch keine libs verwendet habe die mit unbekannten Objekten um sich throw-en.

    Von unbekannten Objekten hat ja keiner was gesagt. Bloss von Klassen die nicht von std::exception abgeleitet sind.

    Was mir auf die Schnelle so einfällt:
    MFC CException*
    ATL CAtlException
    MSVC _com_error
    Xerces-C++ SAXException
    Xerces-C++ DOMException (ist natürlich nicht verwandt mit SAXException )
    Crypto++ [c]Exception[/c]





  • Haha, nein, danke für die Korrektur 🙂

    (Ich hab doof wie ich bin nur im Doxygen geguckt, und da wird die Basisklasse std::exception halt verschwiegen - weil Doxygen wohl konfiguriert wurde externe Klassen hier zu ignorieren.)

    ps: Hab jetzt auch Xerces noch nachkontrolliert, da stimmt es aber (zumindest bei der Version die wir im Einsatz haben).



  • Hab mich auch erst gewundert und dann im Code nachgesehen, wenn es so wäre, dann müssten mir ja jede Menge Exceptions durch die Lappen gegangen sein...



  • happystudent schrieb:

    Shade Of Mine schrieb:

    Weil man höllisch aufpassen muss. Ein zu aggresives catch irgendwo killt dir den ganzen Mechanismus und ist quasi nicht auffindbar.

    Das geht aber nur mit einem catch(...) (der Typ des geworfenen Objekts ist schließlich weggekapselt und nicht sichtbar). Und wenn man nach einem catch(...) nicht re-throwt ist man ja wohl selbst schuld bzw. hat eh ganz andere Probleme.

    Du kannst auch eine andere Exception werfen 😉

    Ist halt dann bloed wenn der Code urspruenglich nicht darauf ausgelegt wurde Abort Exceptions korrekt zu handeln.

    Shade Of Mine schrieb:

    Prinzipiell ist es ein goto und das hat eben gewisse Nachteile.

    Ich finde es ist eigentlich eher ein allgemeineres return , wobei das geworfene Objekt der Rückgabewert ist.

    nein, es ist ein astreines goto.

    Du springst zu einem label (catch) dass irgendwo ist und lustigerweise sogar mehrmals vorkommen kann.

    Man verwendet es, weil die alternativen auch nicht super sind. Aber man muss sich immer vor Augen halten: es ist Missbrauch von Exception.


Anmelden zum Antworten