Fehlerbehandlung in einer Engine



  • Hallo.

    Schreibe gerade an einer 2D-Engine und bin dabei, mich um die Fehlerbehandlung zu kümmern.
    Bis jetzt habe ich das immer über die Rückgaben der Initialisierungs-Funktionen gemacht, was natürlich weder unglaublich komfortabel, noch besonders schön ist.
    Daher möchte ich das nun verbessern und habe folgende Möglichkeiten:

    1.Exceptions einzubauen, die sind ja extra für derartige Aufgaben gedacht. Jedoch kann der Aufwand mit dem Fangen der unterschiedlichen Fehler auch groß werden. Außerden ist die Frage, ob diese auch performant genug sind!?

    2. Mit Signal/Slots arbeiten (boost::signals), so könnte ich die einzelnen Initialisierungsschritte in Slots fassen und dann mit einem "Initialisierungs-Signal" und einem entsprechenden Combiner alle Slots (Initialisierungsfunktionen) durchlaufen und abbrechen, falls ein Fehler auftritt (+ Fehlerausgabe / Logging).
    Dies wäre ziemlich einfach und auch kurz zu implementieren, ist jedoch natürlich nicht extra für so etwas konzipiert, wie es die Exceptions sind.

    Was meint ihr dazu?

    MfG
    Hundefutter



  • Wieso keine normalen Rückgabewerte?
    Exceptions finde ich nicht gut, da diese den Programmfluss unterbrechen, ausserdem kann der übermäßige Genuss von Exceptions zu einer beschissenen API führen, wie CEGUI die viel zu viel Exceptions verwenden mMn.
    Rückgabewerte sind besser. Ausserdem würde ich ein Logbuch anlegen (txt oder html), welches dem Programmierer genauen Aufschluss darüber gibt, was genau schief gelaufen ist. Im Release-Mode kann man ja best. verbose-Meldungen aus der Engine unterdrücken.
    rya.



  • Möchte die normalen Rückgaben (bool) ersetzen, da
    - sie mir die Rückgabe "klauen" bei Funktionen, die eigentlich ein Ergebnis zurückgeben sollen
    - diese immer noch in der Aufrufenden Funktion überprüft werden müssen
    - bei verschiedenen potentiellen Fehlern in einer Funktion mir nicht genau sagen können, was der Fehler war

    Außerdem soll der Programmfluss ja unterbrochen werden, da bei einer fehlgeschlagenen Initialisierung, die man für eine weitere Sache braucht, das Programm stoppen soll, da sonst gewissen Voraussetzungen für den weiteren Ablauf nicht mehr gegeben sind. Breche jetzt ja auch quasi ab, da ich nicht weitermache, sondern gleich mit return aus der Funktion aussteige.

    MfG
    Hundefutter



  • Ich würde auch einfache Rückgabewerte nehmen, einfach ein enum mit Werten deffinieren und die dann zurückgeben. Der rest wäre dann eh Sache des benutzers, das entspräche auch dem Stil von zB DirectX dort wird ja auch nur ein Fehelrcode geliefert und was der Programmierer der das ganze benutzt damit macht (oder nicht) ist dann seine Sache.


  • Administrator

    Ich lege allen bisherigen Postern nahe, die folgenden drei Artikel aus unserem Magazin zu lesen:
    Exception-Handling
    Modernes Exception-Handling Teil 1 - Die Grundlagen
    Modernes Exception-Handling Teil 2 - Hinter den Kulissen

    Grüssli



  • Hundefutter schrieb:

    Möchte die normalen Rückgaben (bool) ersetzen, da
    - sie mir die Rückgabe "klauen" bei Funktionen, die eigentlich ein Ergebnis zurückgeben sollen
    - diese immer noch in der Aufrufenden Funktion überprüft werden müssen
    - bei verschiedenen potentiellen Fehlern in einer Funktion mir nicht genau sagen können, was der Fehler war

    Außerdem soll der Programmfluss ja unterbrochen werden, da bei einer fehlgeschlagenen Initialisierung, die man für eine weitere Sache braucht, das Programm stoppen soll, da sonst gewissen Voraussetzungen für den weiteren Ablauf nicht mehr gegeben sind. Breche jetzt ja auch quasi ab, da ich nicht weitermache, sondern gleich mit return aus der Funktion aussteige.

    MfG
    Hundefutter

    Klar, bei schweren Fehlern ist eine Exception gerechtfertigt. Aber ich kenne APIs die werfen ne Exception, nur weil ein sound nicht geladen wurde, etc. Man muss Exceptions mMn halt mit Bedacht einsetzen und nicht für jeden kleinen Fehler.
    Und es ist auch klar, dass true oder false nichts über den Fehler aussagt.
    Folgender Code schon:

    enum 
    {
       errOK = 0,
       errFILE_NOTFOUND,
       errPOINTER_NULL,
       // TBC
    };
    
    void foobar()
    {
       if ( bar == foo )
       { 
         return errOk;
       }
       return errFILE_NOTFOUND;
    }
    
    // Hast du einen Rückgabewert und willst trotzdem nen Fehlercode übergeben:
    CMyClass fooBAR(int *err)
    {
       if ( bar == foo )
       {
           *err = errOK;
           return new CMyClass;
       }
       return NULL;
    }
    // Wobei in dem Beispiel auch klar ist, dass NULL auch ein "Rückgabewert" sein kann. Man muss den Zeiger halt prüfen.
    

    Man kann für sowas aber keine allgemein gültige Regel aufstellen, sowas muss man methode zu methode entscheiden.
    Ich persönliche verwende Rückgabewerte und sei es NULL oder ein gültiger Zeiger. Tritt ein Fehler auf, der das Fortsetzen des Programmes nicht erlaubt, werfe ich eine Exception.
    rya.



  • @ Dravere

    Das werte ich mal einfach als Stimme für Exceptions..

    Würdest du vielleicht auch noch was zu den größten Vorteilen sagen und möglicherweise Stellung zu der 2. Möglichkeit nehmen?

    MfG
    Hundefutter



  • Hundefutter schrieb:

    Das werte ich mal einfach als Stimme für Exceptions..

    Würdest du vielleicht auch noch was zu den größten Vorteilen sagen und möglicherweise Stellung zu der 2. Möglichkeit nehmen?

    Lies die 3 Artikel - das wird alles dort besprochen. Die Vorteile von Exceptions, die Nachteile, infos ueber fehlerbehandlung per rueckgabetyp und signale. Alles wird behandelt.

    Und nach der Lektuere wirst du wissen, dass Exceptions das richtige sind.

    man muss sich ja nur Scorcher24 code ansehen um zu wissen, dass das nie eine ernsthafte alternative sein kann.



  • Gerade von einem globalen Enum zur Fehlerbehandlung würde ich ganz großen Abstand nehmen. Wenn man den in einem einzelnen Header pflegt, muß jedes Subsystem der Engine den inkludieren. Das heißt, jede Änderung in einem Subsystem mit einem neuen Fehler sorgt für eine Neukompilierung aller Sourcefiles, die den anziehen. Sprich: Alle Subsysteme oder anders formuliert: Rebuild!

    Und man schafft da Abhängigkeiten, die nicht zusammengehören.

    Gruß Kimmi



  • Scorcher24 schrieb:

    Wieso keine normalen Rückgabewerte?
    Exceptions finde ich nicht gut, da diese den Programmfluss unterbrechen, ausserdem kann der übermäßige Genuss von Exceptions zu einer beschissenen API führen, wie CEGUI die viel zu viel Exceptions verwenden mMn.
    Rückgabewerte sind besser.

    Ah, wieder mal ein ganz sachliches und objektives Urteil. Return-Werte sind übrigens das Allerletzte, da der übermässige Gebrauch zu einer blöden API führen kann. Sehr brauchbare Argumentation... 🙄

    Bereits wenn man den Rückgabetypen anderswertig nutzen will oder eine Konstruktion einer Klasse abbrechen muss, sind Exceptions gerechtfertigt. Von dem Zusammenspiel mit RAII und den weiteren in den Artikeln erwähnten Punkten mal abgesehen.

    P.S. Dein Code spricht nicht gerade für dich. Nur schon das Memory Leak, das du erzeugen würdest, wenn der Rückgabetyp der Funktion mit dem Typen des zurückgegebenen Objekts übereinstimmen würde.



  • Ich hab mir das mit den Exceptions mal angeschaut, allerdings erschließt sich mir der Sinn überhaupt nicht. Ich baue zzt an ner Kleinen Engine herum die im Grunde keine Fehlerbehandlung hat und im Grunde nur das nötigste abfängt, was im Moment nicht so tragisch ist da ich die Schwachpunkte ja kenne. Im wesentlichen wäre bei mir der größte Feind eigentlich nur das Fehlen von Dateien bzw NULL-Zeiger, und wenn ich mir die Exceptions so anschaue dann müste ich diese Schlüsselstellen ja jedesmal in einen TryBlock packen und den Fehler bis zur obersten Schicht durchreichen, was ja auch bedeutet das der der die Engine am ende nutzt auch try Blöcke verwenden müsste damit die Nachrichten auch ankomemn und das ganze nicht abraucht. Da finde ich die Variante Logbucheintrag und Rückgabewert über ein Enum auch wesentlich praktischer, weniger Tipparbeit und auch ziemlich sicher.



  • Dravere schrieb:

    Ich lege allen bisherigen Postern nahe, die folgenden drei Artikel aus unserem Magazin zu lesen:
    Exception-Handling
    Modernes Exception-Handling Teil 1 - Die Grundlagen

    Habe mir die Anfaenge durchgelesen. Design by Contract wird von mir auch in groesseren Projekten oder Algorithmen durchweg angewandt. Es folgen nichtssagende oder schlechte Beispiele aus anderen Programmiersprachen fuer Fehlerbehandlung. Es wird auch nicht darauf eingegangen, das Exceptions in C++ andere Aufgaben wahrnehmen als z.B. in Java oder C#. Deswegen werden sie auch anders verwendet.

    Eine Funktion kann auch mittels inout-Parameter Werte zurueckliefern etc. In C bekommt man mittels perror("...") dann zusaetzliche Information. Moeglichkeiten gibt es viele.

    Und nach der Lektuere wirst du wissen, dass Exceptions das richtige sind

    Meistens nicht ... (meine Meinung)



  • Rückgaben kann man auch über Referenzen in der Parameterliste der Funktion erledigen...

    Und bei jedem kleinen Piss gleiche eine Exception zu werfen halte ich auch für übertrieben... Wenn es geht nutze ich auch den return-Value als Rückgabevariante für Fehlercodes. Aber eben nicht als kompletter Ersatz für Exceptions sondern nur in den Fällen wo ein Errorcode per return völlig ausreichend ist.



  • Shade Of Mine schrieb:

    man muss sich ja nur Scorcher24 code ansehen um zu wissen, dass das nie eine ernsthafte alternative sein kann.

    Das kannst du so nicht sagen. Das ist eine gängige Form der Fehlerbehandlung und bevor es Exceptions gab die einzige.
    Aber es ist mal wieder eine Glaubensfrage. Aber fachlich sind beide Möglichkeiten korrekt, da diese vom Compiler zugelassen werden.
    Stil hat jeder einen anderen.
    rya.



  • knivil schrieb:

    Habe mir die Anfaenge durchgelesen.

    Musst schon alles lesen...

    Es folgen nichtssagende oder schlechte Beispiele aus anderen Programmiersprachen fuer Fehlerbehandlung. Es wird auch nicht darauf eingegangen, das Exceptions in C++ andere Aufgaben wahrnehmen als z.B. in Java oder C#. Deswegen werden sie auch anders verwendet.

    Weil exceptions in C++ und C# quasi identisch gehandhabt werden. Und die Unterschiede mit RAII werden auch genannt.

    Eine Funktion kann auch mittels inout-Parameter Werte zurueckliefern etc. In C bekommt man mittels perror("...") dann zusaetzliche Information. Moeglichkeiten gibt es viele.

    Steht alles in den Artikeln und auch warum das ein Käse ist.

    Und nach der Lektuere wirst du wissen, dass Exceptions das richtige sind

    Meistens nicht ... (meine Meinung)

    Nenn mir einen guten Grund.

    @Scorcher24:
    lies die Artikel


  • Administrator

    Für die interne Fehlerbehandlung in einer Engine, da kann man von mir aus Fehlercodes einsetzen. Es ist zwar teilweise ein wenig gefährlich, da man schnell einmal einen Fehler übersieht und dadurch noch mehr Fehler entstehen, vielleicht sogar fatale.
    Sobald man aber aus der Engine raus einen Fehler zurückgibt, sollte man wenn möglich Exceptions verwenden. So kann der Benuzter der Engine sich frei entscheiden, auf welcher Ebene er die Fehlerbehandlung machen möchte. Sowas ist in meinen Augen für den Benutzer viel angenehmer zu handhaben.

    Ich mag Fehlercodes bei eine Engine überhaupt nicht. Die stören immer meinen Programmfluss. Ich will meistens den Fehler nicht dort behandeln, wo er auftrat, sondern weiter vorne.
    Wenn man trotzdem Fehlercodes anbieten möchte, dann schlage ich eine Überladung der Funktion vor, wo man eine Variable übergeben kann, wo dann der Fehlercode reingespeichert wird. Die Funktion hat dann eine nothrow Garantie.

    Das nur als Zusatz für die Geschmacksfrage. Ansonsten verweise ich wirklich auf die Artikel und ein Überfliegen reicht nicht, um diese zu verstehen.

    Grüssli



  • Sehr interressant wie hier gegen Exceptions diskutiert wird von Leuten die von Exceptions nicht mehr als die Syntax kennen 😃 👍



  • Weil exceptions in C++ und C# quasi identisch gehandhabt werden.

    Die Handhabung ist implemetationsspezifisch und steht nicht zur Debatte. Sie werden anders verwendet. Siehe Ressourcemanagement. Falls z.B. eine zu oeffnende Datei nicht existiert, dann wird 'ne Exception geworfen. In C++ nicht. Da es keine Ausnahme im eigentlichen Sinne ist. Exceptions sind in C++ fuer Ausnahmen und eine nicht vorhandene Datei ist keine Ausnahme, sondern vorhersehbar. Klar kann man in C++ das trotzdem so handhaben, aber es ist dann nicht mehr idiomatisches C++.

    Steht alles in den Artikeln und auch warum das ein Käse ist.

    Bin halt anderer Meinung (ohne gleich wertend zu sein). Es ist Kaese, weil die Beispiele so gewaehlt sind. Ich wuerde so nicht programmieren. Auch sehe ich z.B. in Artikel Exception-Handling fuer die Funktion MyCalc(double ValueA, double ValueB), dass das switch sich einfach im Werfen und Fangen der Exceptions verbirgt. Ein eher schlechtes Beispiel.

    Nenn mir einen guten Grund.

    Benutze ich Funktionen oder Klassen aus Bibliotheken (ob in Quell- oder Binaerform), so sind Exceptions stark restriktiv. Im Programmdesign muss ich dann auch Exceptions beruecksichtigen, obwohl das vielleicht garnicht noetig ist.

    So kann der Benuzter der Engine sich frei entscheiden, auf welcher Ebene er die Fehlerbehandlung machen möchte.

    Er hat nicht mehr die Freiheit, auf Exceptions zu verzichten. Andere Varianten der Fehlerbehandlung stehen nicht mehr zur Verfuegung.

    Ein anderer Grund wie oben schon gesagt: In C++ sind Exceptions nicht fuers Resourcemanagement gedacht, sondern fuer echte Ausnahmen.



  • knivil schrieb:

    Falls z.B. eine zu oeffnende Datei nicht existiert, dann wird 'ne Exception geworfen. In C++ nicht. Da es keine Ausnahme im eigentlichen Sinne ist.

    Nein, weil die File-Streams leider gravierende Mängel haben. Eine gut spezifizierte Klasse hat nie einen ungültigen Zustand (d.h. einen Zustand, in dem es illegal ist, eine ihrer Methoden aufzurufen). Bei den Datei-Streams reicht dafür der Aufruf der close()-Methode.

    Das ändert nichts daran, daß in _gutem_ C++-Code Exceptions analog zu C# oder Java verwendet werden.

    knivil schrieb:

    Bin halt anderer Meinung (ohne gleich wertend zu sein).

    Ach?

    knivil schrieb:

    Es folgen nichtssagende oder schlechte Beispiele aus anderen Programmiersprachen fuer Fehlerbehandlung.

    knivil schrieb:

    Benutze ich Funktionen oder Klassen aus Bibliotheken (ob in Quell- oder Binaerform), so sind Exceptions stark restriktiv. Im Programmdesign muss ich dann auch Exceptions beruecksichtigen, obwohl das vielleicht garnicht noetig ist.

    Und in welcher Konstellation sollte das nicht nötig sein?



  • audacia schrieb:

    Das ändert nichts daran, daß in _gutem_ C++-Code Exceptions analog zu C# oder Java verwendet werden.

    Eben nicht. Guter C++ Code hat viel weniger try-catch als vergleichsweise C# und Java.

    knivil schrieb:

    Es folgen nichtssagende oder schlechte Beispiele aus anderen Programmiersprachen fuer Fehlerbehandlung.

    Im Laufe der Zeit hat sich das Prinzip des Exception-Handlings (wörtl. aus dem Englischen übersetzt "Ausnahmebehandlung") durchgesetzt. Es basiert grob gesagt darauf, dass durch einen ungewöhnlichen oder nicht eingeplanten Zustand im Programm Exceptions ausgelöst werden, welche an einer zentralisierten Stelle abgefangen und behandelt werden kann. Es erwies sich als effizient einsetzbar, praktisch vorteilhaft und vor allen Dingen von den Methoden und Algorithmen des Programms gelöste Art der Fehlerbehandlung, wie sie beispielsweise die aus C bekannte Technik - bei welcher anhand des Rückgabewertes einer Funktion der Erfolg derselben geprüft wurde - nicht bot!

    "im laufe der zeit ... es erwies sich ... praktisch ... vorteilhaft" - Ja, das ist nichts sagend. (Falls nicht, dann zeig mir doch mal die stichhaltigen Argumente in diesem Absatz)

    Und in welcher Konstellation sollte das nicht nötig sein?

    Immer dann, wenn ich sauber programmieren moechte.

    Langsam hoert das Argumentieren auf und Suggestivfragen und Polemik ersetzen diese Diskussion. Schoen, dass du auch auf meine anderen Argumente eingehst.


Anmelden zum Antworten