Eure Erfahrungen mit Fehlerbehandlung

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Krishty »

Das ist echt ein dicker Hund … ich dachte bisher, dass Java irgendeine Form von RAII realisieren würde – hättet ihr mir das nicht vor dem „Ist Java wirklich so schlecht?“-Thread zeigen können? :D

Nagut, weiter im Text:
Seraph hat geschrieben:Sprich der User hat die Moeglichkeit zwischen 'Send', 'Cancel' und der Moeglichkeitkeit zu beschreiben, was er denn gerade getan hat. Und ich denke nicht, dass es den User ueberfordern wird.
Ich halte immernoch dagegen. Ich versuche, irgendwelche Zahlen zu dem Thema zu finden, aber immer, wenn ich was mit „x“ eingebe, werden nur Pornos gelistet …
User lesen nie etwas. Nicht, wenn sie entspannt sind, und erst recht nicht, wenn sie gerade wütend sind (wie eben nach einem Absturz). Ich glaube nicht, dass die Entscheidung über den Error-Report dem User von Vista unbewusst entzogen wurde. Ich würde zumindest das x ausblenden und das „Send“ per default aktivieren, oder die Buttons zufällig vertauschen :)

Also gut: Ich denke, für alles vor dem RC-Status reicht es, wenn man bei kritischen Fehlern ein Log anzeigen lässt. (Wie man es nicht machen sollte: einfach schließen und vorher die Zwischenablage mit der Fehlermeldung überschreiben, wie in S.T.A.L.K.E.R. … aber es gab ja zum Glück noch zwei andere Arten, auf die das abstürzen konnte :) ) Danach je nach Kundenkreis und Bezahlung entscheiden – bei Gamern und garnichts wird kein Fenster mit einer Verbeugung drin sein, aber sicher eine Dialogbox, sobald ich sicher bin, dass die sich auch jemand durchliest.
(Wie ist das eigentlich mit Windows Error Reporting? Wenn ich da Einsicht bekäme, käme ich auch ohne Dialogbox an die Abstürze, oder?)
Beim nächsten Start mit der Sitzung weitermachen, soweit möglich, ist gut. Erfordert manchmal mehr, manchmal weniger Logik. Aber falls der Status von vorher überhaupt erst zum Absturz geführt hat, ist es katastrophal … wieder User fragen?!

Achja, ich habe das Fail-Early vergessen (sorry, Alexander) …
… wie sich das einsetzen lässt, hängt stark vom Programmumfang und der Programmdynamik ab. Gehen wir von einem relativ kleinen, dialogbasierten Programm aus, ist es mit einigem Aufwand möglich, alle Ressourcen am Anfang zu sammeln – wenn die Ressourcen in Verzeichnissen gepackt sind, sogar relativ einfach (Checksumme).
Gehen wir aber von dem Umfang eines modernen Computerspiels (1 GiB +) aus, wird es happig, falls nicht zufällig eine Infrastruktur wie Steam bereit steht, die sowieso Buch über alle Ressourcen führt. Da könnte man vielleicht noch mit x64 beikommen – beim Programmstart das komplette Programmverzeichnis in den Speicher einblenden und verifizieren. Das wollte ich sowieso schon immer mal machen :)
Und unmöglich ist es dann, wenn der größte Teil der Abstürze auf das Konto von kaputten Skripten geht – z.B. dass es immer crasht, wenn eine Spielfigur in eine bestimmte Situation kommt. Da ist man einfach machtlos und, je nach Größe der Spielwelt, auch ein kompletter Ast von Speicherpunkten verloren (als Negativbeispiel sei wieder S.T.A.L.K.E.R. mit den kontextabhängigsten Abstürzen dieses Jahrtausends genannt) … ich würde sagen, dass die meisten Games heute und in der Zukunft in die Richtung von 2) (zu groß) und 3) (zu dynamisch) gehen. Und für die kleine Basis der Ressourcen macht man es ja gewissermaßen eh schon, weil man die die ganze Laufzeit über benutzt.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von eXile »

Also meinerseits werden benutzt: Asserts, Logfiles und (was noch gar keiner nannte) eine Konsole. Damit meine ich so ein Teil, wie man es noch aus den guten, alten Zeiten von etwa Q3Test kennt. Wenn ein fataler Fehler auftritt, spring die Konsole auf die volle größe des Fensters, und zeigt als Text an … dass man in der Logdatei gucken soll :P. Sorry, ich war bisher zu faul, eigene Fehlermeldungen zu implementieren.

Exceptions verwende ich, je nach Projekt, manchmal sehr viel, und manchmal gar nicht. Das hängt auch sehr stark von den verwendeten Libraries ab, und wie feingranular ich die Exceptions fangen kann – bzw. ob es überhaupt sinnvolle Exceptions zum Abfangen gibt.

PS: Krishty, du sprichst mir mit deiner Kritik an diesem ganz speziellem Spiel vollkommen aus der Seele ;)
Seraph
Site Admin
Beiträge: 1174
Registriert: 18.04.2002, 21:53
Echter Name: Steffen Engel

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Seraph »

Krishty hat geschrieben:
Seraph hat geschrieben:Sprich der User hat die Moeglichkeit zwischen 'Send', 'Cancel' und der Moeglichkeitkeit zu beschreiben, was er denn gerade getan hat. Und ich denke nicht, dass es den User ueberfordern wird.
Ich halte immernoch dagegen. Ich versuche, irgendwelche Zahlen zu dem Thema zu finden, aber immer, wenn ich was mit „x“ eingebe, werden nur Pornos gelistet …
User lesen nie etwas. Nicht, wenn sie entspannt sind, und erst recht nicht, wenn sie gerade wütend sind (wie eben nach einem Absturz). Ich glaube nicht, dass die Entscheidung über den Error-Report dem User von Vista unbewusst entzogen wurde. Ich würde zumindest das x ausblenden und das „Send“ per default aktivieren, oder die Buttons zufällig vertauschen :)
Und ich bleibe dabei :P, ich finde es besser dem User die Auswahl zu geben, ob er etwas senden moechte oder nicht. Ich finde es auch rechtlich etwas bedenklich, etwas ohne das Einverstaendnis des Users Daten ins Netz zu senden, insbesondere wenn vielleicht noch mehr Daten als nur die reinen App-Daten versendet werden. Wenn man mir z.B. nicht die Wahl ueberlassen wuerde und ich haette noch die Chance dazu, wuerde ich wohl den Crash-Report killen statt auf 'Senden' zu klicken, schon allein aufgrund der Tatsache, dass es mir nicht freigestellt war. Aber wir koennen hier auch gern verschiedener Meinung bleiben, es sei denn Du findest eine Statistik, dass 95% aller Programme den User zwingen, das koennte mich u.U. ueberzeugen. :)
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Krishty »

Seraph hat geschrieben:Wenn man mir z.B. nicht die Wahl ueberlassen wuerde und ich haette noch die Chance dazu, wuerde ich wohl den Crash-Report killen statt auf 'Senden' zu klicken, schon allein aufgrund der Tatsache, dass es mir nicht freigestellt war. Aber wir koennen hier auch gern verschiedener Meinung bleiben, es sei denn Du findest eine Statistik, dass 95% aller Programme den User zwingen, das koennte mich u.U. ueberzeugen. :)
Okay, korrigier mich wenn ich fallsch liege, aber: Seit Vista ist Error Reporting standardmäßig aktiviert. („Default settings: By default, error reporting is enabled on clients running Windows Vista.“) Jedes Mal, wenn eine Anwendung kracht, erzeugt Windows einen Dump (XXX.exe funktioniert nicht mehr — es wird nach einer Lösung gesucht — Informationen über das Problem werden gesammelt) und sendet ihn (möglicherweise zeitverzögert) an Microsoft. Dort kann sich jeder, der dessen Programm zertifiziert ist, registrieren um die Dumps seines Programms einzusehen. Verhindern lässt sich das, indem der User in der Sekunde nach dem Absturz noch schnell auf Abbrechen klickt, die Funktion manuell abschaltet oder während der Windows-Instllation nicht die standardmäßigen Sicherheitsoptionen auswählt. Falls aus dem Web noch mehr Infos angefragt werden als ein simpler Dump, wird der User gefragt, ob dieses Mehr an Inforamtionen übermittelt werden soll.

Daher würde ich zwar nicht sagen, dass 95 % aller Programme ohne Nachfrage einen Fehlerreport senden, aber über 50 % der Abstürze mit steigender tendenz dürfte die Quote heute bestimmt schon liegen. Und in der Windows-Datenschutzerklärung steht drin, dass du einverstanden bist dass MS und den registrierten Partnern diese Infos übermittelt werden. (Dass diese Möglichkeit des Fern-Debugings wegen der Signierung und so für einen Hobbyprogrammierer out of touch ist, muss ich wohl nicht sagen.)

Wenn es einer besser weiß, bitte mit Quelle korrigieren. Habe nämlich Mühe, Material zum Thema zu finden.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 2116
Registriert: 25.02.2009, 13:37

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Alexander Kornrumpf »

Ich habe es nun deaktiviert, vor allem aus dem von Steffen genannten Grund, ich möchte gefragt werden. Wenn es die Option gäbe von "always send" auf "always ask" umzustellen, hätte ich vermutlich die gewählt. Auf die Schnelle habe ich das aber nicht finden können.

Aber nochmal zum Thema ich glaube nach dem ganzen hin und her, eigentlich sind es zwei Fragen:

Sollte der User informiert werden?
Hier würde ich sagen entweder:

mit einer Auswahl was er/sie tun kann um das Problem zu beheben. Windows versucht das an manchen Stellen. Meistens sind das die offensichtlichen Sachen a la "Stellen Sie sicher dass der Drucker auch wirklich eingeschaltet ist" aber ich behaupte mal dass das vielen Menschen hilft.

oder (wenn das nicht geht):

ähnlich wie Firefox mit einer höflichen "Sorry das hätte nicht passieren dürfen, wir versuchen alles damit sie beim Neustart an dieser Stelle fortfahren können" Meldung

jedoch nicht:
mit irgendwelchen Informationen, die der User sowieso nicht versteht, wie in Win98 "Schwerer Ausnahmefehler an Adresse 0xcdcdcdcd" wo alle immer gesagt haben "Wieso Ausnahme? Das passiert doch ständig."

Sollte der Entwickler informiert werden?
Da bin ich Steffens Meinung. Das sollte der User entscheiden. Ich gehe (Stichwort Kontexabhängige Fehler) davon aus dass man in den meisten Fällen relativ viel Kontext braucht um wirklich eine Diagnose stellen zu können und den ggf nicht hat. Deswegen bin ich nicht 100% überzeugt, dass dieses Reporting viel bringt. Vielleicht täusche ich mich aber auch.
dronus
Establishment
Beiträge: 114
Registriert: 11.01.2010, 01:53

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von dronus »

Ich benutzt Java, vielleicht etwas speziell. Ich wandele fast alle Exceptions in RuntimeExceptions um, d.h. dass ich sie nicht catchen muss und die Anwendung aussteigt. So gibts sehr wenig fehlerbehandelnden Code und immer sehr präzise Meldungen.
In bekannt wackelig, aber unwichtigen Codeteilen (z.b. Soundengine, De-Luxe Rendereffekte) fange ich die Exceptions ab, wenn die Funktion ein Signal vom Hauptthread ist, sonst lass ich den Thread abstürzen, dann gibts eben keinen Sound.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Krishty »

Alexander Kornrumpf hat geschrieben:mit einer Auswahl was er/sie tun kann um das Problem zu beheben. Windows versucht das an manchen Stellen. Meistens sind das die offensichtlichen Sachen a la "Stellen Sie sicher dass der Drucker auch wirklich eingeschaltet ist" aber ich behaupte mal dass das vielen Menschen hilft.
Es hilft sogar enorm vielen. Das Problem ist nur, dass der Aufwand dafür riesig ist: Auf Code-Ebene haben wir nur mit Fehlern à la „Time-Out“ und „Zugriff verweigert“ zu tun. Die Fehlerbehandlung muss also um eine Kontextanalyse erweitert werden (Tritt ein Time-Out beim Ansteuern des Druckers auf? Dann ist der Drucker vielleicht nicht eingeschaltet), es müssen in bester Doku-Manier aussagekräftige Dialoge geschrieben, getestet und in alle Produktsprachen übersetzt werden – also ein kompletter Layer um den eigentlichen Code herum. Für Hobby-Spieleentwicklung ist das einfach zuviel des Guten (allein, die möglichen Fehlerursachen direkt im Quellcode zu listen verdoppelt die Dateilänge). Realistisch gesehen bleibt nicht viel mehr als Entschuldigungsfenster :(
Alexander Kornrumpf hat geschrieben:jedoch nicht:
mit irgendwelchen Informationen, die der User sowieso nicht versteht, wie in Win98 "Schwerer Ausnahmefehler an Adresse 0xcdcdcdcd" wo alle immer gesagt haben "Wieso Ausnahme? Das passiert doch ständig."
Zu eXiles Vergnügen wieder S.T.A.L.K.E.R. (sieben Buttons, vier Textfelder, aber nicht der leiseste Hauch einer Ursache):
Bild
Alexander Kornrumpf hat geschrieben:Sollte der Entwickler informiert werden?
Da bin ich Steffens Meinung. Das sollte der User entscheiden. Ich gehe (Stichwort Kontexabhängige Fehler) davon aus dass man in den meisten Fällen relativ viel Kontext braucht um wirklich eine Diagnose stellen zu können und den ggf nicht hat. Deswegen bin ich nicht 100% überzeugt, dass dieses Reporting viel bringt. Vielleicht täusche ich mich aber auch.
Ja, sicher sind nicht alle Reports hilfreich. Bei Microsoft ist der Tenor, dass 80 % der eingehenden Abstürze von 20 % der Bugs ausgelöst werden und dass schon die schiere Masse hilft, diese schwächsten Glieder sehr schnell zu finden. Ohne Zahlen können wir aber alle nur mutmaßen (und entfernen uns auch mal wieder von der Spieleentwicklung).
dronus hat geschrieben:So gibts sehr wenig fehlerbehandelnden Code und immer sehr präzise Meldungen.
Das ist die optimale Mischung … bei mir wird an Ort und Stelle jedes Detail ins Log geschrieben. Die Exception dienen dann nurnoch dazu, das Stack-Unwinding auszulösen und RAII übernimmt den Rest, bis der catch-Block im Einstiegspunkt erreicht wird.
dronus hat geschrieben:In bekannt wackelig, aber unwichtigen Codeteilen (z.b. Soundengine, De-Luxe Rendereffekte) fange ich die Exceptions ab, wenn die Funktion ein Signal vom Hauptthread ist, sonst lass ich den Thread abstürzen, dann gibts eben keinen Sound.
Ich habe weder wackeligen noch unwichtigen Code.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
ponx
Establishment
Beiträge: 217
Registriert: 04.05.2008, 12:52
Echter Name: Andy Ponx
Wohnort: Hamburg
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von ponx »

Schrompf hat geschrieben: Return Codes sind für alle Funktionen, bei denen ein Scheitern ein im normalen Ablauf zu erwartendes Ergebnis ist. Z.B. so Sachen wie "gib mir das XYZ zu diesem Namen" - da kommt halt ein Null-Pointer oder eine Referenz auf ein als invalid markiertes Objekt zurück. Das finde ich einfach bequemer, als einen TryCatch-Block drumrum zu bauen, und ein bisschen Performance spart es auch.
Schrompf, wie macht man sowas konkret, also eine Referenz als invalid markieren ? Ich benutz bei sowas bis heute immer Pointer, weil ich sonst nicht weiß wie ich auf NULL testen soll. Bei enums hab ich das schon so gemacht, also z.B.:

Code: Alles auswählen

enum Familie
{    NULL = 0,
    MUDDI,
    VADDI,
    OMMA,
    OPPA
}
...da klappt das ganz gut, aber was mach ich bei structs und Klassen ?

Noch was zu den Exceptions: Beim Google C++ Style Guide sind sie komplett verboten - das hatte mich gewundert, ich hatte Exceptions auch immer für guten Stil gehalten. Ich selber benutz sie auch so selten wie möglich, weil ich's irgendwie unübersichtlich finde.
Seraph
Site Admin
Beiträge: 1174
Registriert: 18.04.2002, 21:53
Echter Name: Steffen Engel

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Seraph »

@Google: Das erklaert auch, warum ihre Applikationen immer ohne Meldung crashen. :D Na gut, meine Erfahrungen mit Google Apps liegen schon ein paar Jahre zurueck, aber damals habe ich mich echt gefragt, wie die sowas als grosse Firma veroeffentlichen koennen.

@invalide Referenzen: Also so wie Schrompf es geschrieben hat, wuerde ich auf ein Flag in dem Objekt tippen, welches den Status angibt.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Krishty »

ponx hat geschrieben:Schrompf, wie macht man sowas konkret, also eine Referenz als invalid markieren ?
Das Objekt, auf das die Referenz zeigt, ist invalid – nicht die Referenz selber (das ist ja gerade das Gute an Referenzen). Hat nur den Nachteil, dass man dem Objekt damit einen „Zombie State“ geben muss (z.B. nullen und eine IsValid()-Funktion hinzufügen), gegen den man auch hier und da testen muss, was die Programmlogik verkompliziert. Kommt auf den Kontext an, aber in den meisten Fällen ist ein Zeiger auf ein Objekt ohne Zombie State die beste Option.
ponx hat geschrieben:Noch was zu den Exceptions: Beim Google C++ Style Guide sind sie komplett verboten - das hatte mich gewundert, ich hatte Exceptions auch immer für guten Stil gehalten.
Sind sie auch, aber die meisten Leute kommen schlecht darauf klar. Wenn man durch Exceptions RAII erzwingt muss man z.B. die Regeln für die Initialisierungsreihenfolge von Membern kennen und einhalten, und das tun leider immernoch die wenigsten. Dass Visual C++ dann trotz korrekter Verwendung noch Warnungen schmeißt, macht die Sache nicht einfacher.

Edit: Der Style-Guide ächtet auch Operatorenüberladung und RTTI und fordert Init()-Funktionen … wahrscheinlich spielen da auch Vorsichtsmaßnahmen gegen Portierungsprobleme und das Abschätzen von Wirkungen (schließlich pflegt Google auch Betriebssysteme und Low-Level-Code) eine Rolle. Die Vorgaben sollten für PC-Entwicklung etwa so unwichtig sein wie die JSF-Richtlinien – der Style kann sich gern daran orientieren, das Programmdesign bitte bloß nicht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
ponx
Establishment
Beiträge: 217
Registriert: 04.05.2008, 12:52
Echter Name: Andy Ponx
Wohnort: Hamburg
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von ponx »

Krishty hat geschrieben:
ponx hat geschrieben:Schrompf, wie macht man sowas konkret, also eine Referenz als invalid markieren ?
Das Objekt, auf das die Referenz zeigt, ist invalid – nicht die Referenz selber (das ist ja gerade das Gute an Referenzen).
ja, das meinte ich, Danke Krishty ! Mit dem IsValid() ist eine schöne Lösung! wieder was gelernt !
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Helmut »

Also ich finde das Programmieren mit Exceptions eigentlich ziemlich anstrengend.
Man muss immer genau im Auge behalten, was passiert, wenn eine Funktion Exceptions wirft. Man muss auf die Reihenfolge der Definitionen achten. Man muss RAII konsequent durchziehen, also im Grunde für jede API, die es gibt, massig Wrapper bauen. Und wenn man mal am Ende einer Funktion etwas Unübliches mit einer Referenz machen möchte, zB eine temporäre Datei löschen oder den Focus wechseln will, muss man das gleich in einen finally Block packen (den es in C++ lustigerweise nichtmal gibt und durch Tricksereien nachgeahmt werden muss).
Um dann noch einigermaßen sauber zu bleiben, wird man natürlich auch noch die Fehler anderer APIs via Return, GetLastError, andere Exceptiontypen, Structured Exceptions und was es noch so gibt in die eigenen Exceptiontypen umwandeln müssen, um am Ende auch eine vernünftige Meldung ausgeben zu können. Umgekehrt wird man auch konvertieren müssen (was meist garnicht geht), wenn man sich in Rückrufprozeduren von C APIs befindet (zB Windowsprozeduren, DllMain, PlugIns, Treiber).
Es gibt es noch einen Haufen Probleme, weil Exceptions die Standardlibrary voraussetzen und nicht richtig standardisiert sind. Bei verschiedenen Versionen in den Modulen hat man also einige Schwierigkeiten.
Zu guter letzt ist auch ein großes Problem von Exceptions, dass man einem Code, der Exceptions korrekt behandelt, dieses oft nicht ansieht. Bei Fehlercodes sieht man schon von Weitem die if Schachtelungen. Zugegebenermaßen denke ich mir auch oft beim Schreiben dieser if Orgien, ob das nicht einfacher geht, aber Exceptions sind leider nicht die Lösung.

Ciao
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von eXile »

In Bezug auf die von Helmut angesprochenen if-Orgien … häufig findet man ja folgenden Code, gerade wenn man es mit API mit C-Interface zu tun hat:

Code: Alles auswählen

A *a = makeResourceA();

if(!resourceAIsValid(a))
{
	cleanUpResourceA(a);
	return;
}

B *b = makeResourceB();

if(!resourceBIsValid(b))
{
	cleanUpResourceB(b);
	cleanUpResourceA(a);
	return;
}

C *c = makeResourceC();

if(!resourceCIsValid(c))
{
	cleanUpResourceC(c);
	cleanUpResourceB(b);
	cleanUpResourceA(a);
	return;
}
Mit dem hier könnte man das Ganze dann auch so verpacken:

Code: Alles auswählen

A *a = makeResourceA();

if(resourceAIsValid(a))
	FINALLY(cleanUpResourceA(a));
else
	return;

B *b = makeResourceB();

if(resourceBIsValid(b))
	FINALLY(cleanUpResourceB(b));
else
	return;

C *c = makeResourceC();

if(resourceCIsValid(c))
	FINALLY(cleanUpResourceC(c));
else
	return;
Das ist zwar jetzt auch nicht besonders schön, aber es erspart doch ein paar Zeilen und es sollte vom Wartungsaufwand her besser sein.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Krishty »

Helmut hat geschrieben:Man muss immer genau im Auge behalten, was passiert, wenn eine Funktion Exceptions wirft.
Empfinde ich nicht so … in 99 % der Fälle bedeutet das Werfen einer Exception für den Aufrufer, dass es aussieht, als sei der werfende Code nie aufgerufen worden. Ich kenne die Argumentation, dass man ein Auge auf jeden Nebenausdruck werfen muss, der was werfen könnte – die sieht man überall und habe sie nie verstanden … wenn ich eine Datei wegen einer bad_alloc-Exception nicht mappen kann ist es doch egal, ob zuwenig Speicher für die Konstruktion des Dateinamens da ist oder für die Reservierung der Pages, oder? Meistens ist es sogar egal ob der Dateiname ungültige Zeichen enthält, die Datei nicht existiert oder die Welt untergeht. Der Fehler ist längst in die Log-Datei geschrieben und wenn es nicht geht, geht es einfach nicht.
Helmut hat geschrieben:Man muss auf die Reihenfolge der Definitionen achten.
Als ob das was Schlimmes wäre … wenn Dinge logisch aufeinander aufbauen, muss man den Zusammenhang auch im Code wiedergeben, und wenn C-Level-Programmierung was anderes erlaubt sehe ich das eher als Schwäche denn als Stärke. Wenn Dinge voneinander unabhängig sind, kann man auch mit Exceptions beliebige Reihenfolgen wählen.
Helmut hat geschrieben:Man muss RAII konsequent durchziehen, also im Grunde für jede API, die es gibt, massig Wrapper bauen.
Helmut hat geschrieben:Um dann noch einigermaßen sauber zu bleiben, wird man natürlich auch noch die Fehler anderer APIs via Return, GetLastError, andere Exceptiontypen, Structured Exceptions und was es noch so gibt in die eigenen Exceptiontypen umwandeln müssen, um am Ende auch eine vernünftige Meldung ausgeben zu können.
Aus Return-Codes und GetLastError() die Fehler zu extrahieren ist zum einen hochgenerisch und zum anderen müsstest du es bei reiner Verwendung von Return-Codes doch auch machen. Das wenn-einmal-dann-immer-Argument gilt für alle guten Patterns wie const-Correctness genauso … und die Wrapper sind überschaubar – mit einem 20-Zeilen-Template hat man das Nötigste an COM-Objekten automatisiert und für APIs, die Return-Codes benutzen, beschränkt sich das wrappen auf if(FAILED(ReturnCode Result = APICall())) PrintError(Result), throw Foo;. Aber ich muss zugeben, dass meine Erfahrungen sich da auf D3D, libpng und Lua beschränken … gibt sicher auch APIs, wo das schwieriger ist. Kannst du mir eine nennen, wo man tatsächlich Structured Exceptions erwarten muss? Ich kenne die nur als Bug-Symptome, nicht als Kommunikationsmittel :)
Helmut hat geschrieben:Und wenn man mal am Ende einer Funktion etwas Unübliches mit einer Referenz machen möchte, zB eine temporäre Datei löschen oder den Focus wechseln will, muss man das gleich in einen finally Block packen (den es in C++ lustigerweise nichtmal gibt und durch Tricksereien nachgeahmt werden muss).
Kannst du ein Beispiel liefern? Code-lokale temporäre Dateien werden beim Aufruf ihres D’tors gelöscht, afaik macht das die WinAPI sogar automatisch bei CloseHandle(). Ironischerweise ist die einzige Stelle, wo ich bisher finally benötigt hätte, der Wrapper einer C-API gewesen. Da habe ich das Schreiben des Destruktors prokrastiniert :D
Helmut hat geschrieben:Umgekehrt wird man auch konvertieren müssen (was meist garnicht geht), wenn man sich in Rückrufprozeduren von C APIs befindet (zB Windowsprozeduren, DllMain, PlugIns, Treiber). Es gibt es noch einen Haufen Probleme, weil Exceptions die Standardlibrary voraussetzen und nicht richtig standardisiert sind. Bei verschiedenen Versionen in den Modulen hat man also einige Schwierigkeiten.
Ja, für Systemprogrammierung sind Exceptions ja schon aufgrund der schwer abschätzbaren Wirkungen völlig ungeeignet. Für den Einsatz über DLL-Grenzen sowieso by-Design. Wenn man Plugins und Callbacks für fremden Code schreibt, muss man auch nach dessen Regeln spielen – das sagt aber nichts über die Qualität der Regeln aus …
Helmut hat geschrieben:Zu guter letzt ist auch ein großes Problem von Exceptions, dass man einem Code, der Exceptions korrekt behandelt, dieses oft nicht ansieht. Bei Fehlercodes sieht man schon von Weitem die if Schachtelungen.
Code behandelt Exceptions üblicherweise korrekt, indem er sie garnicht behandelt … mit zwei catch-Blöcken auf tausend Zeilen reines C++ bin ich momentan sehr zufrieden.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Helmut »

Es war einmal vor langer Zeit ein kleiner Programmierer, der in einer wunderbaren Welt lebte. Sämtliche Funktionen der Win32 API basierten auf Exceptions und RAII. Er war gerade dabei, die Speicherfunktion von MS Paint zu implementieren, dazu genügten zwei Zeilen:

Code: Alles auswählen

void SaveImageToFile(const string& Filename)
{
	AUTO_HANDLE out = CreateFile(Filename);
	WriteImageToHandle(out);
	//Dtor von AUTO_HANDLE gibt den Handle automatisch frei. Yay!
}
Beide aufgerufenen Funktionen benutzen Exceptions, Fehlerbehandlung und Aufräumarbeiten laufen also völlig von selbst. Er freute sich und wollte schon neue coole Features wie Radiergummis implementieren.
Doch dann kam ein Bugreport von einem Betatester, er habe ein Bild gemalt, wollte speichern, gab einen invaliden Dateinamen ein und Paint verabschiedete sich mit einer Fehlermeldung. Das Bild war futsch. Ups sagt da unser Programmierer, da hat wohl sein Kollege, der SaveImageToFile benutzt, vergessen die Exception rechtzeitig abzufangen. Kein Problem, Doku und Code sind schnell gefixt und Win95 wird releast.
Ein Tag nach Release treffen tausende Bugreports ein: Paint habe zwar eine ordentliche Meldung ausgegeben, die alte Bilddatei bei einem Fehler aber komplett leer gemacht. Wieder eine Data-loss Situation, aber unserem Programmierer fällt schnell eine Lösung für das Problem ein:

Code: Alles auswählen

void SaveImageToFile(const string& Filename)
{
	AUTO_HANDLE out = CreateFile(Temp);
	WriteImageToHandle(out);
	MoveFile(Temp, Filename);
}
Immer noch ein sehr schöner Code, etwas verunsichert testet er ihn aber lieber doch und stellt leider fest, dass er immer fehlschlägt: Temp kann ja garnicht verschoben werden, weil die Datei ja noch zum Schreiben geöffnet ist. Gut, müssen wir wohl den Dtor-Aufruf erzwingen:

Code: Alles auswählen

void SaveImageToFile(const string& Filename)
{
	{
		AUTO_HANDLE out = CreateFile(Temp);
		WriteImageToHandle(out);
	}
	MoveFile(Temp, Filename);
}
Nicht mehr ganz so schön, dafür aber ohne Bugs. Zumindest sieht es so aus...

Long story short: Unzählige Bugreports, 3 Jahre und 10 Morddrohungen später sieht der Code so aus:

Code: Alles auswählen

void SaveImageToFile(const string& Filename)
{
	{
		AUTO_HANDLE out = CreateFile(Temp1);
		WriteImageToHandle(out);
		//Wehe jemand entfernt nochmal diesen Block! Dtor von out muss hier aufgerufen werden
	}

	try
	{
		MoveFile(Filename, Temp2);
	}
	catch(SourceDoesntExist)
	{
		//Datei wird zum ersten Mal gespeichert
	}
	catch(...)
	{
		DeleteFile(Temp1); //TODO: Exception abfangen oder nicht?
		throw;
	}

	try
	{
		MoveFile(Temp1, Filename);
	}
	catch(...)
	{
		try
		{
			MoveFile(Temp2, Filename);//Altes Bild wiederherstellen
		}
		catch(...)
		{
			//Diese Exception abfangen, damit Temp1 garantiert gelöscht wird
		}
		DeleteFile(Temp1);
		throw;
	}
	try
	{
		DeleteFile(Temp2);
	}
	catch(...)
	{
		//Falls es vorher keine Datei unter dem Namen gab, wird Temp2 auch nicht
		//existieren und DeleteFile fehlschlagen
	}
}
Und Windows 98 muss immnoch ohne Radiergummifunktion auskommen. Genervt fragt sich der kleine Programmierer, ob es nicht ein besseres Fehlermodell gibt...


Zum Glück war alles nur ein Traum, in Wahrheit war die erste Version von SaveImageToFile:

Code: Alles auswählen

void SaveImageToFile(const char* Filename)
{
	HANDLE out = CreateFile(Filename);
	WriteImageToHandle(out);
	CloseHandle(out);
}
Und als dem kleinen Programmierer nach 5 Sekunden auffiel, dass er die Fehlerbehandlung vergessen hatte, kommt er nach einer Stunde müheseligen Codens letztlich auf das:

Code: Alles auswählen

bool SaveImageToFile(const char* Filename)
{
	HANDLE out = CreateFile(Temp1);
	if(!out)
		return false;
	if(!WriteImageToHandle(out))
	{
		CloseHandle(out);
		DeleteFile(Temp1);
		return false;
	}
	CloseHandle(out);

	if(!MoveFile(Filename, Temp2) && GetLastError() != SourceDoesntExist)
	{
		DeleteFile(Temp1);
		return false;
	}
	if(!MoveFile(Temp1, Filename))
	{
		MoveFile(Temp2, Filename);//Altes Bild wiederherstellen
		DeleteFile(Temp1);
		return false;
	}
	DeleteFile(Temp2);
	return true;
}
"Das hat ja ewig gedauert", denkt er sicht, "warum hat C kein besseres Fehlermodell? Naja, immerhin habe ich jetzt noch genug Zeit um ein Radiergummi zu implementieren."
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Eure Erfahrungen mit Fehlerbehandlung

Beitrag von Krishty »

Exzellentes Beispiel, danke :)

Ja, wann immer nichts verloren gehen darf ist Stack-Unwinding natürlich der Killer. Da es kein Ersetzen von Dateien gibt, das man als atomar bezeichnen könnte (oder fail-safe Verschieben – geht das mit MoveFileEx(REPLACE_EXISTING)?), versagt hier so ziemlich jedes Pattern inklusive Pimpl. Wirklich interessant.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten