Spiel schreiben um C++ zu erlernen - Exceptions



  • SeppJ schrieb:

    Die vielen Assertions haben den Vorteil, dass man sich ob der korrekten Funktion des Programms relativ sicher sein kann und das sollte doch immer das Hauptziel bei der Entwicklung sein. In der Releaseversion sind diese Tests dann aber nicht mehr vorhanden, man hat also keinerlei Nachteile. Exceptions sollten "richtigen" Ausnahmen in einem ansonsten korrekten Programm vorbehalten sein, nicht zum Aufspüren von Programmierfehlern.

    Klingt zwar schön in der Theorie, funktioniert aber in der Praxis nicht. Bei komplexen Programmen wird es immer irgendwo möglich sein, eine Assertion zu verletzen.

    Das führt dann dazu, dass komplexere Programme (wie GCC) selbst im Release mit Assertions gebaut werden. Die Technik ist nicht schlecht, aber auf Assertions im Release zu verzichten ist kaum möglich.


  • Mod

    Das macht es aber auch nicht falsch, viel mit Assertions zu arbeiten. Eine Assertion schlägt auf einen Programmierfehler an. Da ist ein try..catch nicht das richtige Mittel dafür, auch wenn uns der Standard so schöne Exceptions wie logic_error, invalid_argument & Co. anbietet (benutzt die irgendjemand?).

    Und für mittelgroße Programme funktioniert die Technik mit dem Ausschalten der Assertions im Release auch praktisch sehr gut.



  • SeppJ schrieb:

    Das macht es aber auch nicht falsch, viel mit Assertions zu arbeiten. Eine Assertion schlägt auf einen Programmierfehler an. Da ist ein try..catch nicht das richtige Mittel dafür, auch wenn uns der Standard so schöne Exceptions wie logic_error, invalid_argument & Co. anbietet (benutzt die irgendjemand?).

    Nein, Assertions sind genau der richtige Weg, weil es sich um echte Bugs handelt. Es ist nur kritisch, sie ganz auszuschalten.

    GTK+ geht einen anderen Weg: Fehlgeschlagene Assertions werden nach stderr ausgegeben, haben aber ansonsten keine Konsequenzen.



  • SeppJ schrieb:

    Du sollst viele Assertions benutzen, die während der Entwicklung Logikfehler im Programm selber aufdecken (z.B. falsche Benutzung von Funktionen). Für richtige Laufzeitausnahmen, die auch in einem ansonsten korrekten Programm auftreten können, da sie sich der Kontrolle des Programmierers entziehen, nimmst du Exceptions.

    Danke für die Erklärung.

    Nun weiß ich glaub ich, wieso ich die Antwort von volkard nicht verstanden habe.

    Bei meinen "Fehlern" ging es mir nicht um Programmierfehler oder schwere Laufzeitfehler.

    Ich versuche das Beispiel genauer zu machen:
    In einer Methode wird ein älteres Kamerabild mit einem neuen verglichen. Die Methode soll erkennen, was sich verändert hat. Dafür gibt es ganz bestimmte, vordefinierte Veränderungen die möglich sind und erkannt werden können.
    Diese Veränderungen sind über ein großes Projekt hinweg in einem Enum gespeichert, was von allen verwendet wird und eigentlich nicht geändert werden soll, Beispiel:

    typedef enum
    {
        LEFT,
        RIGHT,
        UP,
        DOWN
    } CHANGES;
    

    Aber es können auch zwei Ausnahmen vorkommen: "Keine Änderung" oder "Es ist so viel geändert worden, dass keine der vordefinierten Veränderungen passt.
    Da das Enum CHANGES nicht geändert werden soll und es außerdem Logiken sind, die nur diese Methode betrifft, bin ich am überlegen, wie ich den Rückgabewert nun richtig mache.

    So könnte im C Style die oben genannte Möglichkeit verwendet werden:

    typedef enum
    {
        SUCCESS,
        NOTHING,
        EXACT_CHANGE_NOT_FOUND
    } CHANGE_STATUS;
    
    CHANGE_STATUS foundChange(const Img old, const Img new, CHANGES &result);
    

    Da aber oben gesagt wurde, dass "jede Methode gibt OK oder ein Fehlerobjekt zurück und der ehemalige Rückgabewert ist eine Referenz als erstes Argument." "NIE NIE NIE" verwendet werden soll, wollte ich nun wissen, was die angemessene C++-Art dafür ist.

    Hier eine Exception werfen die andere mit einem try-catch-Block abfangen sehe ich als falsch an. Es ist ja keine "Exception" im herkömmlichen Sinne.



  • Hambrana schrieb:

    SeppJ schrieb:

    Du sollst viele Assertions benutzen, die während der Entwicklung Logikfehler im Programm selber aufdecken (z.B. falsche Benutzung von Funktionen). Für richtige Laufzeitausnahmen, die auch in einem ansonsten korrekten Programm auftreten können, da sie sich der Kontrolle des Programmierers entziehen, nimmst du Exceptions.

    Danke für die Erklärung.

    Nun weiß ich glaub ich, wieso ich die Antwort von volkard nicht verstanden habe.

    Bei meinen "Fehlern" ging es mir nicht um Programmierfehler oder schwere Laufzeitfehler.

    Ich versuche das Beispiel genauer zu machen:
    In einer Methode wird ein älteres Kamerabild mit einem neuen verglichen. Die Methode soll erkennen, was sich verändert hat. Dafür gibt es ganz bestimmte, vordefinierte Veränderungen die möglich sind und erkannt werden können.
    Diese Veränderungen sind über ein großes Projekt hinweg in einem Enum gespeichert, was von allen verwendet wird und eigentlich nicht geändert werden soll, Beispiel:

    typedef enum
    {
        LEFT,
        RIGHT,
        UP,
        DOWN
    } CHANGES;
    

    Aber es können auch zwei Ausnahmen vorkommen: "Keine Änderung" oder "Es ist so viel geändert worden, dass keine der vordefinierten Veränderungen passt.
    Da das Enum CHANGES nicht geändert werden soll und es außerdem Logiken sind, die nur diese Methode betrifft, bin ich am überlegen, wie ich den Rückgabewert nun richtig mache.

    So könnte im C Style die oben genannte Möglichkeit verwendet werden:

    typedef enum
    {
        SUCCESS,
        NOTHING,
        EXACT_CHANGE_NOT_FOUND
    } CHANGE_STATUS;
     
    CHANGE_STATUS foundChange(const Img old, const Img new, CHANGES &result);
    

    Da aber oben gesagt wurde, dass "jede Methode gibt OK oder ein Fehlerobjekt zurück und der ehemalige Rückgabewert ist eine Referenz als erstes Argument." "NIE NIE NIE" verwendet werden soll, wollte ich nun wissen, was die angemessene C++-Art dafür ist.

    Hier eine Exception werfen die andere mit einem try-catch-Block abfangen sehe ich als falsch an. Es ist ja keine "Exception" im herkömmlichen Sinne.

    Mooomentchen mal!
    Ein schwerer Designfehler, daß der enum CHANGES bereits zu schwach ist bzw daß der enum CHANGES überhaupt benutzt wird.

    **Es ist immer ein verdammt schlechter Ratgeber, nach gutem Stil zu suchen, indem man schaut, wie man ein total verkorkstes Projekt am besten rettet. Schlechter Anfang sorgt zwingend zu einem schlechten Ende.
    **

    In dem Fall fühlt sich

    std::pair<SUCCESS,CHANGES> foundChange(Img const& old,Img const& new);
    

    für mich auf den ersten Blick als geringstes Übel an.

    Vielleicht besser, einen eigenen enum zu bauen, der CHANGES vollständig enthält.



  • Danke für deine Antwort.

    volkard schrieb:

    Ein schwerer Designfehler, daß der enum CHANGES bereits zu schwach ist bzw daß der enum CHANGES überhaupt benutzt wird.

    Was wäre denn eine bessere Lösung?
    Also das zu "schwach" verstehe ich noch in Hinsicht, dass es "NO_CHANGES" enthalten könnte. Allerdings ist "EXACT_CHANGE_NOT_FOUND" so speziell für die Methode und wird von den restlichen Komponenten nicht benötigt, die CHANGES benutzen, dass es meiner Ansicht nach darin nichts verloren hat.
    Was ist an der Angabe mit Enum so schlecht? Wenn du schreibst, dass es ein Designfehler ist CHANGES überhaupt zu verwenden, dann denkst du sicher bereits an eine andere Umsetzungsart. Was wäre da die schönere Alternative? - will ja für die Zukunft lernen.



  • Hambrana schrieb:

    Danke für deine Antwort.

    volkard schrieb:

    Ein schwerer Designfehler, daß der enum CHANGES bereits zu schwach ist bzw daß der enum CHANGES überhaupt benutzt wird.

    Was wäre denn eine bessere Lösung?
    Also das zu "schwach" verstehe ich noch in Hinsicht, dass es "NO_CHANGES" enthalten könnte. Allerdings ist "EXACT_CHANGE_NOT_FOUND" so speziell für die Methode und wird von den restlichen Komponenten nicht benötigt, die CHANGES benutzen, dass es meiner Ansicht nach darin nichts verloren hat.
    Was ist an der Angabe mit Enum so schlecht? Wenn du schreibst, dass es ein Designfehler ist CHANGES überhaupt zu verwenden, dann denkst du sicher bereits an eine andere Umsetzungsart. Was wäre da die schönere Alternative? - will ja für die Zukunft lernen.

    Meinte "CHANGES überhaupt bei dieser konkreten Funktion zu verwenden"



  • Ich weiß, das gehört hier in diesen fremden Thread nicht mehr. Aber ich will nur einmal noch kurz nachfragen, da ich gerade einiges lernen kann:
    Wenn du CHANGES nicht verwenden würdest aber dies die entscheidende Wert ist, weshalb man die Methode aufruft, wie würdest du dies designmäßig richtig machen?
    Wäre dein Vorschlag, "ein eigenes Enum das CHANGES vollständig enthält", der beste Weg? Oder hast du an einen grundlegend anderen Aufbau der Methode gedacht, als du von Designfehler gesprochen hast? Noch ist die Methode nicht fest eingemeiselt und ich kann sich nach belieben anpassen.

    Bin sehr dankbar für eure Tipps. Solang man glaubt etwas richtig zu machen wird man sich nicht verbessern. Deshalb bin ich froh über jede Kritik, um mich und meinen Stil weiterentwickeln zu können.



  • Bei solchen Algorithmen springt doch eher ein Vektor und ein Konfidenzwert raus, oder nicht? 🙂 Hat natürlich nix mit der Lösung hier zu tun, da ja schließlich die Anforderungen genau spezifiziert waren und nur die sprachliche Umsetzung von Belang ist. Da finde ich die zwei enums zuviel des Guten, weil man ja sowieso schon sich gegenseitig ausschließende diskrete und endliche Situationen hat. So muss man sich ja auch unnötigerweise einen Default-Wert für changes überlegen, wenn keine Veränderungen offenbar wurden.



  • Hambrana schrieb:

    Ich weiß, das gehört hier in diesen fremden Thread nicht mehr. Aber ich will nur einmal noch kurz nachfragen, da ich gerade einiges lernen kann:
    Wenn du CHANGES nicht verwenden würdest aber dies die entscheidende Wert ist, weshalb man die Methode aufruft, wie würdest du dies designmäßig richtig machen?
    Wäre dein Vorschlag, "ein eigenes Enum das CHANGES vollständig enthält", der beste Weg? Oder hast du an einen grundlegend anderen Aufbau der Methode gedacht, als du von Designfehler gesprochen hast? Noch ist die Methode nicht fest eingemeiselt und ich kann sich nach belieben anpassen.

    Bin sehr dankbar für eure Tipps. Solang man glaubt etwas richtig zu machen wird man sich nicht verbessern. Deshalb bin ich froh über jede Kritik, um mich und meinen Stil weiterentwickeln zu können.

    Einen enum (mit größerem Wertebereich) zurückzugeben, oder ein pair mit zwei enums, das ist bestimmt der beste Weg, denke ich. Was von beiden besser ist, weiß ich nicht, die geben sich aber nicht viel.



  • Gut vielen Dank!


Anmelden zum Antworten