C++ Exceptions und Relevanz



  • Eisflamme schrieb:

    Aus der Quintessenz ging ja aber auch hervor, dass Exceptions in C++ sehr selten zu sein scheinen.

    Achtung.

    In Java und C++ und sonstwo treten Exceptions gleich häufig auf. Die grundlegende Designprinzipien sind in allen Sprachen gleich.

    In Java sind Exceptions aber "in your face". zB checked Exceptions die dir konstant vor der Nase baumeln. Das fehlende Resourcemanagement dass dich konstant zu try/finally blöcken zwingt, etc.

    In C++ sind Exception einfach im Hintergrund, denn Exception Safety in C++ passiert implizit (dank RAII). Eigentlich will man so gut wie nie ein try/catch in C++ schreiben, in Java ist try/catch an jeder Ecke zu treffen.

    Das liegt auch daran dass Exception in Java flexibler sind was Translation/Propagation betrifft und man mit einer Exception mehr machen kann als in C++.

    Aber werfen tut man gleich häufig.



  • Dann war mein Wissen da wirklich zu vage.

    Trotzdem tu ich mich schwer abzuwägen, wann ich Ausnahmen nutze oder nicht. Ich glaube, ich hatte mich damals auch schwer getan mit:

    http://www.cplusplus.com/reference/stdexcept/invalid_argument/

    Das suggeriert ja, dass man die Exception werfen sollte, wenn das Argument nicht stimmt. Das passt ja zu obigem Beispiel der Division, wenn man einen String übergibt oder so.



  • Eisflamme schrieb:

    Dann war mein Wissen da wirklich zu vage.

    Trotzdem tu ich mich schwer abzuwägen, wann ich Ausnahmen nutze oder nicht. Ich glaube, ich hatte mich damals auch schwer getan mit:

    http://www.cplusplus.com/reference/stdexcept/invalid_argument/

    Das suggeriert ja, dass man die Exception werfen sollte, wenn das Argument nicht stimmt. Das passt ja zu obigem Beispiel der Division, wenn man einen String übergibt oder so.

    Ja, wenn du einen int willst und einen string bekommst ist es ein Fehler. Bei Exception oder nicht im Fehlerfall muss man die Anwendung der Funktion abwägen. Kann man sinnvoll verhindern dass ein String übergeben wird wenn ein int verlangt ist? wenn ja, dann exception.

    Kommt es oft vor dass man einen string übergibt? dann lieber return value.

    Als Beispiel:
    File::Open() wirft eine Exception wenn die Datei nicht geöffnet werden kann.
    File::Find() wirft keine Exception wenn die Datei nicht gefunden wurde.

    Leider ist das keine exakte Wissenschaft, viel davon ist "was sich richtig anfühlt".



  • Ich würde exceptions man halt immer dann verwenden wenn man deren spezielle Funktionalität braucht?

    Also aus mehreren geschachtelten Funktionen direkt rausspringen (das passiert ja auch "selten"). "Ausnahme" heißt ja nicht unbedingt "Fehler", auch wenn sich exceptions dafür anbieten. Zum Beispiel:

    struct worker
    {
        // ...
        std::atomic<bool> stop_flag;
        struct stop_exception : std::exception {};
    
        void stop_point() { if (stop_flag) throw stop_exception(); }
    
        void run()
        {
            for (;;) stop_point(); // Thread kann hier abgebrochen werden
        }
    
        void start() 
        {
            try { run(); } catch (stop_exception &) {}
        }
    };
    

    So mach ich das (wurde mir sogar so ähnlich hier im Forum so empfohlen :D)

    Und einen thread bricht man ja nicht notwendigerweise immer in einem Fehlerfall ab, sondern z.B. wenn der Benutzer eine weitere Verarbeitung abbrechen will.



  • happystudent schrieb:

    So mach ich das (wurde mir sogar so ähnlich hier im Forum so empfohlen :D)

    Als Teil eines Scherzes hoffe ich.

    Sowas würde ich einem Mitarbeiter bei mir nicht durchgehen lassen.



  • Allgemein ist es verpönt Exceptions zu "flow control" Zwecken zu misbrauchen.

    Bei dem speziellen Thema, also Abbruch von Worker-Threads (oder generell Abbruch von lang laufenden Berechnungen), ist es allerdings "allgemein akzeptiert" das über Exceptions zu machen. Siehe z.B. ThreadAbortException oder TaskCanceledException (.NET Framework).

    Sehr praktisch sind Exceptions auch zum Abbruch von rekursiven Suchfunktionen wenn man ein Ergebnis gefunden hat (z.B. Weg aus einem Labyrinth finden). Wobei das ein Anwendungsfall ist den wohl die meisten nicht mehr akzeptieren werden.



  • Shade Of Mine schrieb:

    Als Teil eines Scherzes hoffe ich.

    Naja, eigentlich war ich schon der Meinung dass das ernst gemeint war. Hoffe ich zumindest weil ich jetzt schon mein Programm so aufgebaut habe (und bis jetzt gut damit fahre) 😃

    Shade Of Mine schrieb:

    Sowas würde ich einem Mitarbeiter bei mir nicht durchgehen lassen.

    Warum? Wenn man in der run-Methode sehr viele geschachtelte Unterfunktions-Aufrufe hat ist es doch extrem mühsam im Falle eines Abbruchs alles immer durchzureichen?

    Mit einer exception hat man dagegen eine saubere Lösung und kann sich inder Implementierung auf das wesentliche konzentrieren...



  • wenn man ein plugins system mit dlls baut, sollte man in c++ besser keine excpetions verwenden.



  • Ah, OK - für Thread Beenden ist das eine häßliche Möglichkeit die man gerne aus Bequemlichkeit verwendet. Da kann es OK sein. Es spart Code und das ist es oft Wert.



  • happystudent schrieb:

    Warum? Wenn man in der run-Methode sehr viele geschachtelte Unterfunktions-Aufrufe hat ist es doch extrem mühsam im Falle eines Abbruchs alles immer durchzureichen?

    Threads bricht man eigentlich nur ab, wenn man sie fuer spekulative Ausfuehrung braucht und sie inzwischen nicht mehr braucht. Dann frage ich sie aber ueblicherweise in der aeussersten Schleife ab und nicht so oft zwischendurch.
    Ansonsten sind exceptions in threads auch gefaehrlich, denn wenn man sich gerade in einer critical section befindet, wird zwar der lock_guard geschlossen, aber es koennen noch unvollendete Werte bestehen bleiben.



  • Marthog schrieb:

    Threads bricht man eigentlich nur ab, wenn man sie fuer spekulative Ausfuehrung braucht und sie inzwischen nicht mehr braucht.

    Und zwar immer so, dass sich ein Thread selbst beendet. Von außerhalb sollte er nur das Signal bekommen, dass er (bald) sterben soll. Einen Thread zu killen ist immer problematisch, da man nie weiß was er gerade macht.



  • Andromeda schrieb:

    Und zwar immer so, dass sich ein Thread selbst beendet. Von außerhalb sollte er nur das Signal bekommen, dass er (bald) sterben soll. Einen Thread zu killen ist immer problematisch, da man nie weiß was er gerade macht.

    Ich mein ja auch nicht von killen, die exception wird schon von dem thread selbst geschmissen.

    Shade Of Mine schrieb:

    Ah, OK - für Thread Beenden ist das eine häßliche Möglichkeit die man gerne aus Bequemlichkeit verwendet. Da kann es OK sein. Es spart Code und das ist es oft Wert.

    Ja, aber das Problem warum man das da macht ist ja eigentlich unabhängig von dem Thread-Zeug: Man will halt nicht manuell aus etlichen Unterfunktionen rausgehen sondern direkt, das meinte ich.

    Deswegen sehe ich exceptions halt auch nicht nur als Fehler-Behandlungs-Mechanismen - ein throw ist ja auch eigentlich überhaupt nicht an eine exception gebunden. Man kann schließlich auch beliebige Objekte werfen (die dann auch nicht mehr das Wort "Ausnahme" mit sich tragen).

    Das gleiche Problem kann ich ja auch ohne threads haben:

    struct tier
    {
        struct fressfeind_alarm {};
    
        void such_nach_essbarem()
        {
            if (fressfeind_in_sicht())
                throw fressfeind_alarm(); // lass alles stehn und liegen
        }
    
        void suche_essen()
        {
            geh_in_gefaehrliches_gebiet();
            such_nach_essbarem();
            geh_in_sicheres_gebiet();
            schlaf_ein_paar_stunden();
        }
    
        void mach_was() 
        {
            while (true) {
                try { 
                    suche_essen() 
                } 
                catch (fressfeind_alarm &) { 
                    lauf_weg(); 
                }
            }
        }
    };
    

    Hier will man ja auch nicht unbedingt durch alle Funktionen zurückschleusen dass alles abgebrochen werden soll. Im Gegenteil, mMn passt eine exception (bzw. ein throw) hier perfekt zu der Modellierung des Tagesablaufs.



  • happystudent schrieb:

    Ja, aber das Problem warum man das da macht ist ja eigentlich unabhängig von dem Thread-Zeug: Man will halt nicht manuell aus etlichen Unterfunktionen rausgehen sondern direkt, das meinte ich.

    Das sollte aber nicht vorkommen. Thread beenden ist so eine Ausnahme Sache da man einen Thread manchmal eben killen will. Aber Prinzipiell ist flowcontrol mit Exceptions richtig mies.

    Nicht nur weil es lahm hoch 3 ist sondern auch weil es schwer nachverfolgbar ist und korrektheit nur schwer nachweisbar ist.

    Im Gegenteil, mMn passt eine exception (bzw. ein throw) hier perfekt zu der Modellierung des Tagesablaufs.

    Nein, hier ist das Design unpassend.

    Hier brauchst du ein Signal/Event basierstes System. Denn du willst auf Events (Feind gesichtet, Futter gesichtet, Wetter hat sich geändert, Es ist Nacht geworden, Magen voll, etc.) reagieren und da sind Exception einfach eine schlechte Lösung.

    Deshalb bin ich auch gegen das Thread-Beispiel. Es ist Excpetion-Missbrauch. Manchmal muss man Sachen missbrauchen um unmengen an Code zu sparen, aber Missbrauch bleibt Missbrauch und bitte bloß nicht in andere Situationen übertragen.



  • happystudent schrieb:

    Ja, aber das Problem warum man das da macht ist ja eigentlich unabhängig von dem Thread-Zeug: Man will halt nicht manuell aus etlichen Unterfunktionen rausgehen sondern direkt, das meinte ich.

    Wie setjmp/longjmp, die gibt es schon seit den C-Tagen.
    C++-Exceptions eröffnen dem Programmierer einen alternativen Ausführungspfad. Sie sind im Grunde eine Untergruppe der Coroutines. Nur dass es keinen Rückweg ins bisherige Level (wie finally in Java) gibt.



  • 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. Das ist quasi der größere, noch hässlichere, Bruder von goto. Besonders toll ist wenn man setjmp/longjmp aus einem C++ Projekt heraus nutzen muss (libpng jemand?). Man muss quasi alle Objekte die einen eigenen Destruktur definieren weit von dem Ganzen fern halten weil diese sonst im Falle eines longjmp nicht richtig zerstört werden. RAII kann man mal total vergessen.



  • 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. Das ist quasi der größere, noch hässlichere, Bruder von goto.

    So ist es. 😃

    C ist für echte Männer.
    C++ ist für Hausfrauen.

    Ernsthaft: wer beides beherrscht, ist gut aufgestellt. Wer aber nur auf C++ setzt und dabei C verteufelt, hat den geraden Weg in die Verdammnis eingeschlagen.


  • Mod

    Ernsthaft: wer beides beherrscht, ist gut aufgestellt. Wer aber nur auf C++ setzt und dabei C verteufelt, hat den geraden Weg in die Verdammnis eingeschlagen.

    Quatsch.



  • Arcoth schrieb:

    Ernsthaft: wer beides beherrscht, ist gut aufgestellt. Wer aber nur auf C++ setzt und dabei C verteufelt, hat den geraden Weg in die Verdammnis eingeschlagen.

    Quatsch.

    QFT!!!


  • Mod

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

    Quatsch.



  • Arcoth schrieb:

    Ernsthaft: wer beides beherrscht, ist gut aufgestellt. Wer aber nur auf C++ setzt und dabei C verteufelt, hat den geraden Weg in die Verdammnis eingeschlagen.

    Quatsch.

    Das liegt vielleicht daran, dass viele C++-Apologeten gehaupten, C++ wäre ein besseres C.


Anmelden zum Antworten