Fehlerbehandlung in einer Engine
-
Knivil und ich sind von massivem Exception-Einsatz nicht überzeugt.
Shade of Mind auch nicht. Ich finde es aber gut, dass die Diskussion von der reinen Polemik zurueck zu Argumenten gefunden hat. Und NULL ( oder 0 ) ist immer noch besser als gar nix.
-
it0101@loggedoff schrieb:
...siehe letzter Beitrag von Tachyon.
Doch. Du scheinst zu denken, der Sinn von Exceptions liegt darin, ein Programm durch den massiven Einsatz von try-catch-Blöcken irgendwie lauffähig zu halten. Zumindest lässt einen Deine Interpretation des von Dir zitierten Artikelschnippsels das vermuten.
Das ist aber nicht der Sinn. Der Sinn ist es, das Programm in einem validen Zustand zu halten. Ist eine Exception im Konext eines Programms so schwerwiegend, dass es nicht mehr valide laufen kann, so muss die Exception zum Abbruch des Programms führen. In dem Fall lässt man sie bis zur obersten Instanz durchfallen. Im Falle von C++ als entweder in etwas wiecatch(...){cerr << "Serious error occurred, terminating...\";}
an höchster Stelle, oder gleich ans OS.
-
Ist eine Exception im Konext eines Programms so schwerwiegend, dass es nicht mehr valide laufen kann, so muss die Exception zum Abbruch des Programms führen.
Da geh ich absolut mit.
Begründet aber immer noch nicht, warum man Exceptions an jeder Ecke benutzen muss und Rückgabewerte in Form von Errorcodes generell scheisse sein sollen.
-
Shade Of Mine schrieb:
Ich stimme Knivil eigentlich fast komplett zu, ein Rückgabewert kann abgefangen werden, muß aber nicht, wenn sich der Nutzer Sicher ist das seine angaben korrekt sind kann er ihn Ignorieren, so wie es einige Leute ja bei DX auch tun zB.
Deshalb gelten Spiele generell auch als so Stabil, nicht wahr?Fehler darf man nie ignorieren und das ist der Punkt. Natürlich ist es einfach sie zu ignorieren, aber das beste kommt jetzt erst:
exception erlauben es dir sie zu ignorieren und nur an einer zentralen stelle dich mit ihnen zu befassen.Naja wer sagt dennd as man sie Ignoriert? ob ich einen Rückgabewert habe und denn gegenteste oder ob ich die Exception verarbeite läuft aufs gleiche hinaus, Fehlerbehandlung. Und die Zentrale Stelel ist nicht imemr das richtige, ich habe zB nur 3 Probleme die Auftreten können, NULL-Zeiger, fehlende Dateien oder das irgendeine der vielen DX Funktionen mal streikt, das gibt nen Eintrag ins Log mit genauen infos und dann halt ne Fehlerrückgabe und auf die muß man sofort Reagieren, wenn du 10 Models lädst und eins davon is nicht da dann musste ja direkt handeln da kannste ja nicht warten bis alles fertiggerendert ist und dir die Zugriffsverletzungen um die Ohren fliegen. Mir fällt auch ehrlich gesagt kein Fehler ein bei dem man hätte Zentral reagieren können, dazu fehlt mir evtl die Erfahrung was so alles Fehelr evrursachen kann.
Shade Of Mine schrieb:
Du hast einfach nur nicht meine Artikel gelesen.
try/catch hast du so gut wie nie. Meistens habe ich ein try/catch in main() und dann vielleicht auf 5000 LOC ein try/catch - wenn überhaupt.
Doch ich habe deine Artikel gelesen, den 1. und 2. komplett den Hinetr den Kulissen hab ich angefangen, allein die vielen Blöcke haben mich dann entgültig abgeschreckt weil ich mir dann dachte das kanns ja nicht sein, nen ganzen haufen Blöcke nur für die paar Zeilen Code das schreckt mich ab, warum ist ganz einfach, ich kann jede Menge Blöcke bauen oder eben einfach sagen ERR_FILE_NOT_FOUND, wovon letztere Variante genauso aussagekräftig ist wie die Exceptions, und sie ist direkt zu sehen, man muß sich da nicht durchkämpfen.
-
it0101@loggedoff schrieb:
Ist eine Exception im Konext eines Programms so schwerwiegend, dass es nicht mehr valide laufen kann, so muss die Exception zum Abbruch des Programms führen.
Da geh ich absolut mit.
Begründet aber immer noch nicht, warum man Exceptions an jeder Ecke benutzen muss und Rückgabewerte in Form von Errorcodes generell scheisse sein sollen.
Das Auswerten von Rückgabewerten kann man einfach vergessen. Exceptions muss man bewusst verwerfen, sonst merkt sie spätenstens das OS.
Deine Aussage:it0101@loggedoff schrieb:
Wo geworfen wird, muss auch abgefangen werden
Ohne das Werfen von Exceptions spart man auch die try-catch-Geschichte.Meine Aussage:
Wo geworfen wird, kann man auch abfangen und behandeln. Fängt man die Exception nicht ab, ist sie nicht behandelbar und das Programm bricht ab. Man kann sie die "try-catch-Geschichte" sparen.PS: Führt man eine Operation durch, die bei einem Fehler gesichert nicht zu einem fehlerhaften Programmzustand führt, so halte ich entsprechende Rückgabewerte für okay. Ansonsten sollte man doch lieber Exceptions werfen.
-
it0101@loggedoff schrieb:
Ist eine Exception im Konext eines Programms so schwerwiegend, dass es nicht mehr valide laufen kann, so muss die Exception zum Abbruch des Programms führen.
Da geh ich absolut mit.
Begründet aber immer noch nicht, warum man Exceptions an jeder Ecke benutzen muss und Rückgabewerte in Form von Errorcodes generell scheisse sein sollen.
Das seh ich genauso (BEIDE PUNKTE). Werde mir die Artikel aber auch nochmal genauer zu gemüte führen...
rya.
-
PS: Führt man eine Operation durch, die die bei einem Fehler gesichert nicht zu einem fehlerhaften Programmzustand führt, so halte ich entsprechende Rückgabewerte für okay. Ansonsten sollte man doch lieber Exceptions werfen.
Mehr wollte ich eigentlich auch nicht vermitteln.
Ich wehre mich hier nur gegen das verallgemeinernde Verdammen von ErrorCode-Rückgaben. Das ist nämlich Quatsch.
Wenn allerdings das Programm aufgrund von Fehlern unter keinen Bedingungen weiterlaufen kann, dann muss eine Exception geworfen werden.
@Tachyon
wie ich bereits vorhin sagte: Eigentlich liegen wir garnicht so weit auseinander.(von Shade mal abgesehen
)
-
Xebov schrieb:
Shade Of Mine schrieb:
Ich stimme Knivil eigentlich fast komplett zu, ein Rückgabewert kann abgefangen werden, muß aber nicht, wenn sich der Nutzer Sicher ist das seine angaben korrekt sind kann er ihn Ignorieren, so wie es einige Leute ja bei DX auch tun zB.
Deshalb gelten Spiele generell auch als so Stabil, nicht wahr?Fehler darf man nie ignorieren und das ist der Punkt. Natürlich ist es einfach sie zu ignorieren, aber das beste kommt jetzt erst:
exception erlauben es dir sie zu ignorieren und nur an einer zentralen stelle dich mit ihnen zu befassen.Naja wer sagt dennd as man sie Ignoriert? ob ich einen Rückgabewert habe und denn gegenteste oder ob ich die Exception verarbeite läuft aufs gleiche hinaus, Fehlerbehandlung. Und die Zentrale Stelel ist nicht imemr das richtige, ich habe zB nur 3 Probleme die Auftreten können, NULL-Zeiger, fehlende Dateien oder das irgendeine der vielen DX Funktionen mal streikt, das gibt nen Eintrag ins Log mit genauen infos und dann halt ne Fehlerrückgabe und auf die muß man sofort Reagieren, wenn du 10 Models lädst und eins davon is nicht da dann musste ja direkt handeln da kannste ja nicht warten bis alles fertiggerendert ist und dir die Zugriffsverletzungen um die Ohren fliegen. Mir fällt auch ehrlich gesagt kein Fehler ein bei dem man hätte Zentral reagieren können, dazu fehlt mir evtl die Erfahrung was so alles Fehelr evrursachen kann.
Ja, dir fehlt es an Erfahrung. In einem real-world Programm und eine Bibliothek ist immer eine solche, bestehen deine Methoden zu einem großen Teil aus Code der Daten validiert und Fehler entsprechend behandelt. Man könnte sich viel Arbeit sparen, wenn die vielen großen Bibliotheken mit denen man alltäglich zu tun hat selbst Exceptions benutzen würden, nur ist das leider oft nicht der Fall, da diese Bibliotheken nicht C++-spezifisch sind, oder schon sehr alt sind und man früher aus anderen Gründen auf Exceptions verzichten musste.
DirectX Fehler lassen sich grob in zwei Kategorien einteilen: Programmierfehler (leicht zu beheben nach einem Blick auf die Fehler-Beschreibung von DirectX bzw. kurzem Googeln) und "device-lost". Letzteres kann so gut wie immer passieren, deshalb muss man auch von jedem DirectX Aufruf den Rückgabewert überprüfen.
DirectX ist überhaupt ein sehr schlechtes Beispiel, da Code der mit DirectX arbeitet nie schön ist, sondern völlig zugekleistert von Fehlerbehandlungscode wird. Ein dünner Wrapper und der Einsatz von Exceptions kann hier wahre Wunder wirken, probier es doch einmal aus
Danach kann dein Code so aussehen wie in den ganzen schlechten Spieleprogrammierbücher/-tutorials nur mit dem Unterschied, dass sämtliche Fehler sauber behandelt werden
-
it0101@loggedoff schrieb:
[...]
(von Shade mal abgesehen
)
Mit dem Unterschied, dass ich (und viele andere) sofort bestätigen würden, dass Shade verdammt viel Ahnung vom Programmieren hat.
Hört einfach mal auf ihn, im schlimmsten Fall lernt ihr was dazu
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.
-
it0101@loggedoff schrieb:
[Wenn allerdings das Programm aufgrund von Fehlern unter keinen Bedingungen weiterlaufen kann, dann muss eine Exception geworfen werden.
Der springende Punkt ist eigentlich, dass auch dann, wenn es unter manchen Bedingungen weiterlaufen kann eine Exception geworfen werden muss. Es gibt ja noch mindestens einen Fall, wo es nicht weiterlaufen kann.
-
Hört einfach mal auf ihn, im schlimmsten Fall lernt ihr was dazu
Ich weiß selber, dass er ne Menge Plan hat.
Aber er hat eben auch eine Meinung. Und nur weil er vielleicht in nicht so schlimmen Fällen ( wo das Programm eben weiterlaufen kann ) Exceptions verwendet wo andere eben ErrorCode nutzen, dann heißt das noch nicht, dass seine Lösung die einzig wahre ist.Aber das ist eben genau wie mit der uneingeschränkten Nutzung der STL, die hier gern vertreten wird - es gibt immer mehrere Ansichten und es muss nicht immer sein, dass eine der beiden Ansichten komplett und in allen Fällen falsch ist.
Aber einfach zu sagen: ihr habt keine Ahnung, und ich hab Recht ist nicht die feine Art zu diskutieren.
-
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.
-
dito.... ich bin seit meinem Wechsel auf C / C++ noch nie auf die Idee gekommen irgendwo rumzuspringen
-
it0101@loggedoff schrieb:
dito.... ich bin seit meinem Wechsel auf C / C++ noch nie auf die Idee gekommen irgendwo rumzuspringen
damals in Basic.... naja egal. Vergessen wir dieses dunkle Zeitalter
-
Xebov schrieb:
Naja wer sagt dennd as man sie Ignoriert? ob ich einen Rückgabewert habe und denn gegenteste oder ob ich die Exception verarbeite läuft aufs gleiche hinaus,
Nur ist der Weg ein anderer.
Bei Error Codes musst du immer und überall ifs einbauen. IMMER.
bei Exceptions hast du 0 Fehlerbehandlungscode im normalen Code drinnen.if(!InitDevice()) { return ID_FAILED; } if(!SetupScene()) { return SS_FAILED; } if(!RunLoop()) { return RL_FAILED; }
versus
InitDevice(); SetupScene(); RunLoop();
Sollte erkennbar sein was schöner ist, oder?
ich habe zB nur 3 Probleme die Auftreten können, NULL-Zeiger, fehlende Dateien oder das irgendeine der vielen DX Funktionen mal streikt, das gibt nen Eintrag ins Log mit genauen infos und dann halt ne Fehlerrückgabe und auf die muß man sofort Reagieren, wenn du 10 Models lädst und eins davon is nicht da dann musste ja direkt handeln
Natürlich muss ich direkt handeln - aber nicht im LoadModel() sondern am komplett anderen Ende der Anwendung. Nämlich dort wo ich etwas tun kann. zB den User nach dem richtigen Verzeichnis fragen oder eine neuinstallation verlangen, etc.
LoadModel selber weiss aber nix davon. Es sagt nur "kann nicht laden" und in einem komplett anderen Modul reagiere ich auf den Fehler. Und zwar jetzt und sofort - nur eben nicht dort wo der Fehler auftrat sondern dort wo ich ihn behandeln kann.Doch ich habe deine Artikel gelesen, den 1. und 2. komplett den Hinetr den Kulissen hab ich angefangen, allein die vielen Blöcke haben mich dann entgültig abgeschreckt weil ich mir dann dachte das kanns ja nicht sein, nen ganzen haufen Blöcke nur für die paar Zeilen Code das schreckt mich ab, warum ist ganz einfach, ich kann jede Menge Blöcke bauen oder eben einfach sagen ERR_FILE_NOT_FOUND, wovon letztere Variante genauso aussagekräftig ist wie die Exceptions, und sie ist direkt zu sehen, man muß sich da nicht durchkämpfen.
Kapier ich nicht. Hast du folgenden Code gelesen?
class Class { private: char* name; int* array; public: Class(char const* name) : name(0), array(0) { try { this->name = new char[strlen(name)+1]; array = new int[100]; } catch(std::bad_alloc& e) { delete [] this->name; delete [] array; throw; } strcpy(this->name, name); fill(array); } //... };
Alternativ auch mit if/else und 0 statt bad_alloc vorstellbar.
Aber dank exceptions kann ich daraus folgendes machen:
class Class { private: std::string name; std::vector<int> array; public: Class(char const* name) : name(name), array(100) { fill(array); } //... };
Und das ist es, was man verstehen muss.
Diese kurze Variante von Class - sie ist nur mit exceptions möglich.Exceptions erlauben es uns massig Code zu sparen und dadurch den Code klarer gestalten zu können.
Exceptions haben übrigens nichts mit der Schwere eines Fehlers zu tun. Bei manchen Fehlern ist ein sofortiges terminate() sogar das sinnvollste. Sie haben mit der Art eines Fehlers zu tun. Erkläre ich übrigens in den Artikel
Die schwere eines Fehlers wird übrigens auch nur vom Kontext bestimmt...
-
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..