Fehlerbehandlung in einer Engine



  • Scorcher24 schrieb:

    Ähm tschulligung, aber ich arbeite ja qausi immer mit ErroCodes (als Enum, ich rede hier nicht unbedingt von bool) und hab noch NIE ein goto gebraucht...
    rya.

    Das bedeutet du hast manchmal code verdopplung.



  • Scorcher24 schrieb:

    Wenn man sauber mit Errorcodes arbeiten will muss man zwangsweise mit goto arbeiten und man fängt an einen schlechten Exceptionmechanismus nachzubauen, wo ist also der Vorteil von Errorcodes, wenn man Exceptions bereits in der Sprache zur Verfügung hat? Und ich rede hier von Errorcodes, nicht von einem Bool, oder auch mal einem TriBool, für spezielle Funktionen.

    Ähm tschulligung, aber ich arbeite ja qausi immer mit ErroCodes (als Enum, ich rede hier nicht unbedingt von bool) und hab noch NIE ein goto gebraucht...
    rya.

    Wie löst Du dann z.B. folgendes Szenario elegant:

    • Es sollen u.U. unterschiedliche 10 Ressourcen (welche explizit wieder freigegenen werden müssen) allokiert werden.
    • Für einen validen Zustand müssen entweder alle 10 Ressourcen erfolgreich allokiert wurden sein, oder aber gar keine.
    • Beim Allokieren der 6. Ressource tritt ein Fehler auf, wodurch die bereits allokierten 5 Ressourcen wieder freigegeben werden müssen.
    • Der Fehler kann auch bei jeder anderen der 10 Allokationen auftreten.
    • Die Allokation der Ressourcen erfolgt mittels einer Funktion, welche im Fehlerfall einen entsprechenden Fehlercode zurück gibt.
    • Nach einem halben Jahr müssen drei weitere Ressourcen hinzugefügt werden, für welche die bereits genannten Punkte ebenfalls gelten.

    ?
    Mich interessiert vor allem die Freigabe der Ressourcen.

    Edit: kleiner Zusatz in Punkt 1



  • Tachyon schrieb:

    Mich interessiert vor allem die Freigabe der Ressourcen.

    RAII

    Nachtrag:

    aus einem

    template<typename ForwardIterator, typename T> 
    void uninitialized_fill(ForwardIterator first, ForwardIterator last, T const& obj) { 
        ScopeGuard guard = MakeGuard(makeDelayedRangeExecute(first, destroy)); 
        while(first!=last) { 
            new (&*first) T(obj); 
            ++first; 
        } 
        guard.Dismiss(); 
    }
    

    wird dann eben ein

    template<typename ForwardIterator, typename T> 
    bool uninitialized_fill(ForwardIterator first, ForwardIterator last, T const& obj) { 
        ScopeGuard guard = MakeGuard(makeDelayedRangeExecute(first, destroy)); 
        while(first!=last) { 
            if(!new (&*first) T(obj))
              return false;
            ++first; 
        } 
        guard.Dismiss(); 
        return true;
    }
    

    aber man sieht sehr schön dass wir plötzlich mehr code haben 😉



  • Shade Of Mine schrieb:

    Tachyon schrieb:

    Mich interessiert vor allem die Freigabe der Ressourcen.

    RAII

    Die Anwendung stelle ich mir aber ohne das Werfen von Exception nicht so einfach vor...



  • Zugegeben. In dem Fäll wäre der Code sicherlich zumindest unschön, aber ich denke mit Exceptions und einem try-catch wirds auch nicht viel besser..



  • alloc10resources
    {
      10 Lokale Objekte;
      b = true;
      b = b && alloc( Resource1 )
      b = b && alloc( Resource2 )
      ...
    
      if( b )
        copy resources or whatever smart_ptr
    }
    

    Z.B. so?



  • it0101@loggedoff schrieb:

    Zugegeben. In dem Fäll wäre der Code sicherlich zumindest unschön, aber ich denke mit Exceptions und einem try-catch wirds auch nicht viel besser..

    Bei einem throw räumt mir aber RAII alle vorher allokierte wieder auf. Man mus quasi nichts mehr machen. Im catch-Block sowieso nicht.



  • Tachyon schrieb:

    Shade Of Mine schrieb:

    Tachyon schrieb:

    Mich interessiert vor allem die Freigabe der Ressourcen.

    RAII

    Die Anwendung stelle ich mir aber ohne das Werfen von Exception nicht so einfach vor...

    Ja, es kostet mehr Code. Aber statt einem throw machst du halt return.

    Was dabei natürlich suckt ist die transaltion von low level auf high level error code. RAII ermöglicht es mit exception eben 0 fehlerbehandlungscode schreiben zu müssen. RAII mit error Codes ermöglicht es nur auf aufräumungsarbeiten verzichten zu können (die fehlerbehandlung selber muss man immer noch schreiben).

    Deshalb ist RAII mit exceptions so stark. ideal wäre natürlich wenn uncaught_exception überall funktionieren würde, dann wäre RAII noch um ein eck stärker...



  • knivil schrieb:

    alloc10resources
    {
      10 Lokale Objekte;
      b = true;
      b = b && alloc( Resource1 )
      b = b && alloc( Resource2 )
      ...
    
      if( b )
        copy resources or whatever smart_ptr
    }
    

    Z.B. so?

    Und woher weisst Du, bei welchem alloc das ganze daneben ging?



  • Shade Of Mine schrieb:

    ...Ja, es kostet mehr Code. Aber statt einem throw machst du halt return.

    Okay, das geht natürlich. Wobei man bei da bei Konstruktoren auf Fehlerinformationen verzichen muss. 🤡



  • Ist doch egal. Resourcendestruktor kuemmert sich um die korrekte Freigabe, RAII halt. Kannst auch wahlweise im Log nachsehen. alloc10res kann einfach b zurueckliefern.



  • knivil schrieb:

    Ist doch egal. Resourcendestruktor kuemmert sich um die korrekte Freigabe. Kannst auch wahlweise im Log nachsehen.

    Problem ist aber, woher weiss die ressource dass sie sich zerstören muss?

    Wenn ich ein LoadAllModels() mache - und beim 7. sterbe ich - woher wissen die anderen 6 dass sie ebenfalls sterben müssen?

    für sowas hat man denn etwas ähnliches wie mein DelayedRangeExecute (im prinzip also einen ScopeGuard) - aber ich habe das gefühl ich greife etwas zu weit voraus 😉

    Schau dir aber mal meinen Code an - und vergleiche die exception und die return code variante. Welche ist besser?

    Der Punkt ist:
    du hast mit exceptions weniger code zu schreiben und verwässerst dir den code nicht mit fehlerbehandlung. nebenbei sind dann noch so ein paar sachen nett ala schutz vor murphy - wenn du mal vergisst auf einen fehler zu reagieren, etc.



  • Die Ressource weiss, das sie geladen wurde oder nicht geldaen wurde. Ist 'nen Objekt und zustandsbehaftet. Sie weiss es einfach. Z.B. bool Texture.load(...) legt Speicher an kopiert Texturdaten rein. Naja, ist was fehlgeschlagen, dann ist der Speicherpointer halt 0. Lade ich jetzt meine 10 Texturen wird nur im Erfolgsfall kopiert, smart_ptr whatever ... . Falls nicht, wird der Destruktor der Resourcen aufgerufen.

    Btw. ich weiss schon was es mit RAII, Gards etc. auf sich hat. Aber es ging ja um Exceptions. Wenn bool Texture.load( ... ) 'ne Exception werfen wuerde, dann waere ich gezwungen mittels try-catch diese zu fangen. Hier gebe ich einfach nur false zurueck. Soll der Benutzer der Funktion sich darum kuemmern. (Nein es ist nicht der Anwender, sondern der Programmierer, der meine Funktion benutzt, meist ich selber).



  • knivil schrieb:

    Die Ressource weiss, das sie geladen wurde oder nicht geldaen wurde. Ist 'nen Objekt und zustandsbehaftet. Sie weiss es einfach. Z.B. bool Texture.load(...) legt Speicher an kopiert Texturdaten rein. Naja, ist was fehlgeschlagen, dann ist der Speicherpointer halt 0. Lade ich jetzt meine 10 Texturen wird nur im Erfolgsfall kopiert, smart_ptr whatever ... . Falls nicht, wird der Destruktor der Resourcen aufgerufen.

    Und wer ruft den Destruktor in welchem Fall auf, und wie stellst Du sicher, dass die Destruktoren im Erfolgsfall nicht beim Verlassen des Scopes aufgerufen würden, was Deine Smartpointer invalidieren würde?

    knivil schrieb:

    ...Soll der Benutzer der Funktion sich darum kuemmern. (Nein es ist nicht der Anwender, sondern der Programmierer, der meine Funktion benutzt, meist ich selber).

    Verstehe ich nicht. Wo ist der Unterschied zu Exceptions? Da kannst Du auch sagen, "soll sich der Benutzer drum kümmern". Du musst die Exception nicht fangen. Du musst Ihm nur sagen, dass eine bestimtme Exception geworfen werden kann, genauso wie Du Ihm sagen musst was es bedeutet, wenn Deine Funktion false zurückgibt.



  • Hui, da habe ich ja ganz schön was losgetreten 🙄

    Naja, jetzt kann ich mich auf jedenfall nicht beklagen, dass ich zu wenig Rückmeldungen hatte und das Thema nicht genug diskutiert wurde :).

    MfG
    Hundefutter

    P.S.: Diskutiert noch schön weiter, ist sehr interessant, aber bitte lasst alle am Leben 😉



  • Verstehe ich nicht. Wo ist der Unterschied zu Exceptions?

    q.e.d 😃



  • knivil schrieb:

    Die Ressource weiss, das sie geladen wurde oder nicht geldaen wurde. Ist 'nen Objekt und zustandsbehaftet. Sie weiss es einfach. Z.B. bool Texture.load(...) legt Speicher an kopiert Texturdaten rein. Naja, ist was fehlgeschlagen, dann ist der Speicherpointer halt 0. Lade ich jetzt meine 10 Texturen wird nur im Erfolgsfall kopiert, smart_ptr whatever ... . Falls nicht, wird der Destruktor der Resourcen aufgerufen.

    Aufpassen.
    Es geht darum WANN der Dtor aufgerufen werden soll. Und das muss er manchmal eben sofort (rollback) und nicht später.

    Btw. ich weiss schon was es mit RAII, Gards etc. auf sich hat. Aber es ging ja um Exceptions. Wenn bool Texture.load( ... ) 'ne Exception werfen wuerde, dann waere ich gezwungen mittels try-catch diese zu fangen. Hier gebe ich einfach nur false zurueck. Soll der Benutzer der Funktion sich darum kuemmern. (Nein es ist nicht der Anwender, sondern der Programmierer, der meine Funktion benutzt, meist ich selber).

    Nein. exceptionsicherheit hat nichts aber auch rein garnichts mit try/catch zu tun.

    Schau dir mal die codes in meinem artikel an - wieviele try/catch siehst du? Im idealfall hast du exakt 1 try/catch in deinem programm.



  • Ich kenne den Unterschied zwischen exceptionsicher und und Exceptions. Aber ich sollte doch ein Beispiel fuer 10 Resourcen ohne Verwendung von Exceptions fuer Tachyon liefern ... dat isses. Ja wenn sofort ein Rollback durchgefuehrt werden soll, dann ... muss ich drueber nachdenken. In dem Artikel kommen sehr wenig try/catch vor, aber ich bin da noch von dem anderen Artikel gepraegt ...

    Verstehe ich nicht. Wo ist der Unterschied zu Exceptions?

    Ich benutze keine und zwinge den Benutzer/Programmierer nicht dazu, welche zu benutzen. Wenn er dann eine Exception basierend auf der Rueckgabe werfen moechte, kann er gern machen.



  • knivil schrieb:

    Aber ich sollte doch ein Beispiel fuer 10 Resourcen ohne Verwendung von Exceptions fuer Tachyon liefern ...

    Dein Beispiel funktioniert aber nicht, bzw. macht keinen Sinn.



  • knivil schrieb:

    aber ich bin da noch von dem anderen Artikel gepraegt ...

    Dann wird es Zeit dass du dir modernes Exceptionhandling ansiehst und mir Gründe nennst warum error Codes besser sind.

    Vorallem solltest du mal darüber meditieren:

    Es gibt 3 Probleme mit diesem Code.

    1. Die Fehlerbehandlung erfolgt lokal und macht dadurch den Code schwerer lesbar.
    2. Jede Funktion verlangt andere Behandlung von Fehlern.
    3. In Sprachen ohne Garbage-Collector muss man seinen eigenen Mist selbst wegräumen. Dieser Cleanup-Code ist kompliziert einzubauen. Hier z.B. über hässliche gotos gelöst. Alternativ auch mit tief verschachtelten ifs lösba

    Punkt 3 ist mit RAII lösbar, aber Punkt 1 und 2 liegen schwer.


Anmelden zum Antworten