Fehlerbehandlung in einer Engine



  • Xebov schrieb:

    [...]Hier mit Smart Pointern zu Hantieren wäre ganz schon aufwendig allein beim Gedanken das die Objekte einfach so im Abfluß verschwinden könnten, oder eben auchmal garnicht verschwinden wird mir irgendwie schlecht.

    Es ist aber bei jedem Smartpointer-Typen klar definiert, zu welchem Zeitpunkt das verwaltete Objekt zerstört wird. "Einfach so" oder "gar nicht" passiert da nichts.



  • Xebov schrieb:

    nicht im Konstruktor sondern in eienr Init methode und hat auch eine ShutDown methdoe, sollte Init erfolgreich gewesen sein würde der Konstruktor automatisch ShutDown aufrufen und alles aufräumen. Hier mit Smart Pointern zu Hantieren wäre ganz schon aufwendig allein beim Gedanken das die Objekte einfach so im Abfluß verschwinden könnten, oder eben auchmal garnicht verschwinden wird mir irgendwie schlecht.

    Keine init Methoden verwenden sondern im Konstruktor die Arbeit machen.

    Ich wette mit dir, du reduzierst den code dadurch ein gutes Stück.

    Wenn du RAII konsequent einsetzt, dann sparst du dir enorm viel arbeit...

    OK, das klingt ja shconmal nicht schlecht wegen einiger Beispiele bin ich davon ausgegangen das man Exceptions überall von Hand weiterreichen muß, wenn die Exception aber einfach in nen Fluß fällt und bis sie irgendwo abgefangen wird treibt dann ist das ja garnichtmal so schlecht.

    Genau so funktioniert es auch. Exceptions werden einfach weiter gereicht und alle RAII Objekte werden korrekt zerstört.

    Im idealfall hat man deshalb wie gesagt nur ein try/catch im ganzen Programm... (praktisch gesehen gibt es natürlich immer wieder stellen wo man fehler dann doch jetzt behandeln muss - aber der fehlerbehandlungs code ist im vergleich zu einer error code variante verschwindend gering)



  • Shade Of Mine schrieb:

    Keine init Methoden verwenden sondern im Konstruktor die Arbeit machen.

    Ich wette mit dir, du reduzierst den code dadurch ein gutes Stück.

    Wenn du RAII konsequent einsetzt, dann sparst du dir enorm viel arbeit...

    Naja so einafch ist das nicht, bei Modellen und effekten zB würde es bei mir gehen, alelrdings würde das die Wiederverwendbarkeit einschränken, die Init Variante die ich evrwende stützt sich auf eine bool Variable, im Konstruktor wird sie Falls, wenn Init abgeschlossen ist wird sie true, wird die Klasse dann gelöscht ohne ShutDown aufzurufen (was sie false machen würde) dann würde der destruktor automatisch ShutDown aufrufen. Mein größtes Problem wären aber meine Verwaltungsstützen, die sind nämlich Singleton Klassen, da ist der Konstruktor ja etwas beschrängt.

    Shade Of Mine schrieb:

    Genau so funktioniert es auch. Exceptions werden einfach weiter gereicht und alle RAII Objekte werden korrekt zerstört.

    Im idealfall hat man deshalb wie gesagt nur ein try/catch im ganzen Programm... (praktisch gesehen gibt es natürlich immer wieder stellen wo man fehler dann doch jetzt behandeln muss - aber der fehlerbehandlungs code ist im vergleich zu einer error code variante verschwindend gering)

    Naja ganz so leicht stell ich mir das nicht vor wenn man zB Load Model nimmt und das ganze etwas verschachtelt ist weil da viele Klassen am werkeln sind müsste man schon jeden Aufruf in einen Block einpacken, da käme man nicht drumherum, aber es klingt auf jedenfalls chonmal besser als es der Artikel suggeriert hat.



  • Xebov schrieb:

    Naja ganz so leicht stell ich mir das nicht vor wenn man zB Load Model nimmt und das ganze etwas verschachtelt ist weil da viele Klassen am werkeln sind müsste man schon jeden Aufruf in einen Block einpacken

    Warum? Wenn beim Laden irgendwo was schief geht (egal auf welcher Ebene), dann ist doch eh das gesamte Modell im Eimer. Dann kannst Du RAII alles wieder aufräumen lassen, und irgendwo an oberster Stelle eine Fehlermeldung o.ä generieren.



  • Tachyon schrieb:

    Warum? Wenn beim Laden irgendwo was schief geht (egal auf welcher Ebene), dann ist doch eh das gesamte Modell im Eimer. Dann kannst Du RAII alles wieder aufräumen lassen, und irgendwo an oberster Stelle eine Fehlermeldung o.ä generieren.

    Naja das ist so nicht ganz richtig, nur wiel Teile schiefgehen muß nicht alles Schiefgehen, nehmen wir zB ne Textur ist nichtd a, dann wäre das Modell in Ordnung weil das Modell aber die texturen lädt gäbe es nur die Möglichkeit entweder zu sagen ein Teil fehlt also mache ich nichts, oder eine Standartbehandlung hinzustellen die eine Exception vermeidet dafür aber den Anwende rzwingen würde zusätzliche Daten in die Klassen zu Packen, zB übr eine static Variable.

    Abe rim wesentlichen meinte ich das shcon so wie du man müsste eben beim aufrufen des Modelladens einfach nen tryBlock außenrum Packen.



  • Xebov schrieb:

    Naja das ist so nicht ganz richtig, nur wiel Teile schiefgehen muß nicht alles Schiefgehen, nehmen wir zB ne Textur ist nichtd a, dann wäre das Modell in Ordnung weil das Modell aber die texturen lädt gäbe es nur die Möglichkeit entweder zu sagen ein Teil fehlt also mache ich nichts, oder eine Standartbehandlung hinzustellen die eine Exception vermeidet dafür aber den Anwende rzwingen würde zusätzliche Daten in die Klassen zu Packen, zB übr eine static Variable.

    Nix static variable oder so.

    Du musst nur definieren ab wann ein Modell in einem gültigen Zustand ist. Ist ein Modell ohne Texturen gültig?

    Das ist die Frage.

    Was man zB machen kann ist wenn die eine Texture nicht lädt eine standard texture zu laden oder ähnliches.

    im prinzip ist das alles aber kein technisches problem sondern eine definitionsfrage. wenn du definiert hast was gültig ist und was nicht, dann ist der rest einfach.

    ich habe aber eher das gefühl aktuell ignorierst du das fehlschlagen vom laden einer textur. aber auch das ist mit exceptions möglich...



  • Xebov schrieb:

    Shade Of Mine schrieb:

    Keine init Methoden verwenden sondern im Konstruktor die Arbeit machen.

    Ich wette mit dir, du reduzierst den code dadurch ein gutes Stück.

    Wenn du RAII konsequent einsetzt, dann sparst du dir enorm viel arbeit...

    Naja so einafch ist das nicht, bei Modellen und effekten zB würde es bei mir gehen, alelrdings würde das die Wiederverwendbarkeit einschränken, die Init Variante die ich evrwende stützt sich auf eine bool Variable, im Konstruktor wird sie Falls, wenn Init abgeschlossen ist wird sie true, wird die Klasse dann gelöscht ohne ShutDown aufzurufen (was sie false machen würde) dann würde der destruktor automatisch ShutDown aufrufen. Mein größtes Problem wären aber meine Verwaltungsstützen, die sind nämlich Singleton Klassen, da ist der Konstruktor ja etwas beschrängt.

    Siehst du, genau deshalb solltest du dich wirklich nochmal mit den Kernkonzepten RAII und Exceptions in C++ beschäftigen. Für diesen aufwendigen bool-Mechanismus, ob ein Objekt jetzt korrkt konstruiert wurde, gibt es in C++ eine einfache Lösung. Und ja, sie lautet Exceptions im Fehlerfall werfen und RAII-Objekte benutzen. Wurde ein Objekt im Konstruktor "halb" konstruiert und es fliegt eine Exception, wird es auch wieder "halb" zerstört. Und das ganze voll automatisch ohne extra Zeile Code. Recht praktisch, oder?

    Gruß
    Don06



  • Shade Of Mine schrieb:

    im prinzip ist das alles aber kein technisches problem sondern eine definitionsfrage. wenn du definiert hast was gültig ist und was nicht, dann ist der rest einfach.

    ich habe aber eher das gefühl aktuell ignorierst du das fehlschlagen vom laden einer textur. aber auch das ist mit exceptions möglich...

    Ich ignoriere zzt alles an Fehlern, deswegen beschäftige ich mich ja mit dem thema und verfolge den thread um nen geigneten weg zu fidnen das ganze auf bzw umzurüsten.

    Naja definition ist so eine Sache, das Problem daran ist für mich ja folgendes, wirft die Textur eine Exception dann ist es ihr ansich ja erstmal egal wer der Auftraggeber des vorgangs war dh hat eine andere Klasse den Auftrag erteilt oder der Nutzer selbst, was mich zu dem Problem führt das man die Ausnahmebehandlung direkt in den Auftraggeber wie in diesem Fall den Modelloader einbinden müsste. denn wenn die Exception aus dem Model einmal raus ist müsste man ja erstmal geeignete Methoden einbauen die es ermöglichend en Fehler im top-level zu behandeln.

    Don06 schrieb:

    Für diesen aufwendigen bool-Mechanismus, ob ein Objekt jetzt korrkt konstruiert wurde, gibt es in C++ eine einfache Lösung.

    Aufwendig ist er garnicht, eine Variable und 3 stellen Code an der sie gesetzt wird sind nicht aufwendig, immerhin ermöglicht es so ein System die ganzen Objekte zu erstellen und sie hinterher zu Initialisieren, denn wenn ich im Konstruktor direkt zB eine Modeldatei anfordere dann gibts ein paar Probleme beim erstellen. Eine Init Variante ist nicht schlecht, denn damit kann man das Objekt erstellen und getrennt davon Initialisieren außerdem bliebe das ganze im Falle eienr exception Gültig, der Konstruktor tut nicht viel und das ModelObjekt bräuchte eine getrennte initialisierung, wenn irgendetwas schiefläuft stürzt die Initalisierung aber eben nicht das Objekt es wäre nach einer neuerlichen initalisierung gültig.



  • Xebov schrieb:

    Naja definition ist so eine Sache, das Problem daran ist für mich ja folgendes, wirft die Textur eine Exception dann ist es ihr ansich ja erstmal egal wer der Auftraggeber des vorgangs war dh hat eine andere Klasse den Auftrag erteilt oder der Nutzer selbst, was mich zu dem Problem führt das man die Ausnahmebehandlung direkt in den Auftraggeber wie in diesem Fall den Modelloader einbinden müsste. denn wenn die Exception aus dem Model einmal raus ist müsste man ja erstmal geeignete Methoden einbauen die es ermöglichend en Fehler im top-level zu behandeln.

    Warum ist das kompliziert?

    LoadModel lädt das Modell + alle Texturen. Wenn eine textur nicht lädt, dann default textur laden, lädt die auch nicht dann die exception weiter reichen an den anrufer und irgendwer wird sich verantwortlich fühlen und eine fehlermeldung ausgeben.

    Aufwendig ist er garnicht, eine Variable und 3 stellen Code an der sie gesetzt wird sind nicht aufwendig, immerhin ermöglicht es so ein System die ganzen Objekte zu erstellen und sie hinterher zu Initialisieren, denn wenn ich im Konstruktor direkt zB eine Modeldatei anfordere dann gibts ein paar Probleme beim erstellen.

    Welche Probleme denn?

    Init Methoden sind fast immer eine schlechte wahl.



  • Shade Of Mine schrieb:

    [...]

    Init Methoden sind fast immer eine schlechte wahl.

    Nicht unbedingt, im Falle von DirectX sogar sehr sinnvoll. Allerdings würde ich die Methode nicht Init nennen, sondern OnDeviceLost bzw. Reset, nämlich in diesem Fall muss man die Daten neu laden (und man hat nicht immer die Modell-Datei noch im Speicher geschweige denn die Texturen).

    Xebov, schau dir doch einmal das DXUT Sample Framework an und nutze das zur groben Orientierung, gerade solche Fehlerfälle und Windows-Fallstricke werden dort vorbildlich behandelt (auch wenn ich den Coding-Style nicht mag) 🙂



  • Tippgeber schrieb:

    Shade Of Mine schrieb:

    [...]

    Init Methoden sind fast immer eine schlechte wahl.

    Nicht unbedingt, im Falle von DirectX sogar sehr sinnvoll. Allerdings würde ich die Methode nicht Init nennen, sondern OnDeviceLost bzw. Reset, nämlich in diesem Fall muss man die Daten neu laden (und man hat nicht immer die Modell-Datei noch im Speicher geschweige denn die Texturen).

    Xebov, schau dir doch einmal das DXUT Sample Framework an und nutze das zur groben Orientierung, gerade solche Fehlerfälle und Windows-Fallstricke werden dort vorbildlich behandelt (auch wenn ich den Coding-Style nicht mag) 🙂

    Zur Klarstellung: ich meine nicht die Fehlerbehandlung an sich, sondern, dass sie Fehler behandeln bzw. welche möglichen Fehler sie behandeln.

    Besonders interressant ist auch die Render-Loop, echt erstaunlich was da alles schief gehen kann.



  • Shade Of Mine schrieb:

    Warum ist das kompliziert?

    LoadModel lädt das Modell + alle Texturen. Wenn eine textur nicht lädt, dann default textur laden, lädt die auch nicht dann die exception weiter reichen an den anrufer und irgendwer wird sich verantwortlich fühlen und eine fehlermeldung ausgeben.

    Ja genau das, nur die muß ja erstmal dahin deswegen ja auch die Idee mit der static Variable die eine Standart textur hält.

    Shade Of Mine schrieb:

    Welche Probleme denn?

    Init Methoden sind fast immer eine schlechte wahl.

    Nehmen wir mal folgende Sache An, ich habe mehrere Modelle, sagen wir 5, die will ich alle Laden, der einfachheit halber mache ich zB ein Array, wenn ich Init verwende kann ich new Model[5] machen und sie danach einzelln mit Init Initialisieren, wenn ich kein init habe dann muß ich sie einzelln erstellen und Initialisieren weil sie der Konstruktor will.

    Ich fidne auch Init Methoden generell nicht schlecht, meine Klassen überschreiben beim erstellen im Konstruktor nur einige Variablen mit 0en und setzen einige andere, das kann eigentlich fats nicht shcief gehen, damit würde die Konstruktion eines Objekts immer gelingen, wenn Init Fehlschlägt, das die meiste Arbeit macht könnte man ne Exception werfen und einfach den Zustand des konstruktors wieder herstellen damit würde ein fehlschlag beim Laden der Datei zB trotzdem ein Unitialisiertes Model hinetrlassen das keine Korrupten Daten hat, wenn ich im Konstruktor ne exception habe könnte ich das Objekt ja im Grunde direkt wegwerfen und neu erstellen weil der Zustand irgendwo gelandet ist.

    Das beispiel mit den fehlern werd ich mir direkt mal anschaun danke für den Tip.



  • Nachtrag:
    Gerade bin ich wieder über ein Stück Code gestolpert, das hervorragend demonstriert, warum Exceptions besser sind.
    http://blogs.msdn.com/oldnewthing/archive/2004/07/20/188696.aspx



  • Tja, ich habe schon Code gesehen, der optisch genauso aussieht, nur anstatt if mit try/catch arbeitet (z.B. "generieter" Code von Fujaba). Es gibt auch Beispiele wo Exceptions schneller sind, z.B. komplexe Baumtraversierung auf der Such nach einem bestimmten Knoten. Anstatt staendig Rueckgabewerte abzufragen, wird einfach eine Exception bei Erfolg geworfen und das Suchergebnis dort drin verpackt. Das gelinkte Beispiel habe ich nur ueberflogen und vielleicht ist auch eine bessere Loesung ohne Exceptions moeglich. Ausserdem ist es etwas unserioes, schlechte Programmbeispiele herzunehmen und die Vorteile von Exceptions dadurch zu begruenden. Prinzipiell habe ich nichts gegen Exceptions, aber ich bin eher vorsichtig. Sie schuetzen auch nicht vor schlechten Programmierstil.



  • knivil schrieb:

    Tja, ich habe schon Code gesehen, der optisch genauso aussieht, nur anstatt if mit try/catch arbeitet

    Dann hat jemand try/catch und RAII nicht verstanden.

    knivil schrieb:

    Das gelinkte Beispiel habe ich nur ueberflogen und vielleicht ist auch eine bessere Loesung ohne Exceptions moeglich. Ausserdem ist es etwas unserioes, schlechte Programmbeispiele herzunehmen und die Vorteile von Exceptions dadurch zu begruenden.

    Es ist ein Extrembeispiel, dennoch kann es prozedural niemals auf eine Weise vereinfacht werden, die Exceptions und RAII anbieten.

    knivil schrieb:

    Prinzipiell habe ich nichts gegen Exceptions, aber ich bin eher vorsichtig. Sie schuetzen auch nicht vor schlechten Programmierstil.

    Nein, aber sie ermöglichen einen sauberen Programmierstil, der wenn man erst einmal passende RAII-Objekte hat sich auch deutlich besser lesen und warten lässt (In dem von audacia gezeigten Negativbeispiel würde mittels RAII vermutlich nur eine Verschachtelungsebene => Ein try-catch Block stehen [wenn überhaupt an dieser Stelle sinnvoll]).

    cu André



  • Dann hat jemand try/catch und RAII nicht verstanden.

    Ich weise einfach nochmal darauf hin, dass ich RAII auch mit if machen kann und ich es deswegen nicht als Argument zaehle. Sind gute Gruende fuer Exceptions, ohne gleich RAII ins Bot zu holen, schwer zu finden?

    Es ist ein Extrembeispiel, dennoch kann es prozedural niemals auf eine Weise vereinfacht werden, die Exceptions.

    Wage Behauptung, leider habe ich keine Lust dir das Gegenteil zu programmieren.

    aber sie ermöglichen einen sauberen Programmierstil

    Und if verhindert keinen sauberen Programmierstil (RAII siehe Anfang). So, what's the point?

    (Zumal das Beispiel kaum was mit sauberer Programmierung und C++ zu tun hat. Suchen wir uns einen schwachen Gegener (wie dieses Programm) und bauen unsere Argumentation darauf auf. Ein elegantes Beispiel, was durch Exceptions noch eleganter wird, ist wesentlich ueberzeugender.)



  • knivil schrieb:

    Dann hat jemand try/catch und RAII nicht verstanden.

    Ich weise einfach nochmal darauf hin, dass ich RAII auch mit if machen kann und ich es deswegen nicht als Argument zaehle. Sind gute Gruende fuer Exceptions, ohne gleich RAII ins Boot zu holen, schwer zu finden?

    Hast du schon einmal daran gedacht, dass Exceptions ohne RAII keinen Sinn machen könnten?

    Im ürbigen kann es uns doch herzlich egal sein ob du krampfhaft dein Code mit ifs zukleistern willst.



  • @mein-fan:
    Exceptions machen durchaus Sinn ohne RAII. Trotzdem wuerde ich fuer das Negativbeispiel keine Exceptions einsetzen, RAII vielleicht. "krampfhaft dein Code mit ifs zukleistern" tue ich nicht, es gibt so viele Moeglichkeiten if's zu vermeiden.



  • knivil schrieb:

    Exceptions machen durchaus Sinn ohne RAII.

    M.M.n. nur in Verbindung mit "finally" (was es unter C++ ja so nicht gibt).



  • knivil schrieb:

    Tja, ich habe schon Code gesehen, der optisch genauso aussieht, nur anstatt if mit try/catch arbeitet (z.B. "generieter" Code von Fujaba). Es gibt auch Beispiele wo Exceptions schneller sind, z.B. komplexe Baumtraversierung auf der Such nach einem bestimmten Knoten. Anstatt staendig Rueckgabewerte abzufragen, wird einfach eine Exception bei Erfolg geworfen und das Suchergebnis dort drin verpackt.

    Der Schuß kann aber doch auch sehr schnell mal nach hinten losgehen oder?


Anmelden zum Antworten