www.mikrocontroller.net

Forum: PC-Programmierung Frage zu Exceptions


Autor: Niklas Gürtler (erlkoenig)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Verschiedene Programmiersprachen haben Exceptions. Wie die funktionieren 
und wie man die benutzt habe ich verstanden. Allerdings will mir nicht 
so recht einleuchten, warum deren Verwendung sinnvoll ist:
Wenn eine Funktion verschiedene Operationen durchführt, bei denen Fehler 
auftreten können (Datei öffnen, Daten lesen, Daten schreiben, ...) 
könnte diese Funktion einen großen try-catch-Block verwenden, um alle 
Fehler in diesen einzelnen Operationen abzufangen. Dabei wäre es wohl 
sinnvoll, wenn der catch-Block die Änderungen an den Daten, mit denen 
die Funktion hantiert, wieder rückgängig macht oder, falls das nicht 
funktioniert oder nicht möglich ist, die Daten in einen definierten 
"Reset-Zustand" versetzt, damit derjenige, der die Funktion in seinem 
Code verwendet (auch über mehrere Aufrufs-Ebenen hinweg), sicher weiß, 
was im Fehlerfall mit den Daten passiert. Doch der catch-Block weiß ja 
gar nicht, welche der Operationen fehl geschlagen ist, und kann daher 
die zuvor erfolgreich durchgeführten Operationen nicht rückgängig 
machen. Man braucht also bei jedem Aufruf einer Unter-Operation einen 
eigenen catch-Block, der die Änderungen wieder zurücknimmt. Aber das 
wäre ganz schön umständlich, sodass die Verwendung von Rückgabewerten 
zur Fehlererkennung doch einfacher gewesen wäre. Pseudocode-Beispiel:
void KonvertiereDatei () {
Eingabe-Datei öffnen.
try {
  Ausgabe-Datei öffnen.
} catch {
  Eingabedatei schließen.
  throw;
}
try {
  Daten lesen.
  Daten schreiben.
} catch () {
  Eingabedatei schließen. Ausgabedatei schließen. Ausgabedatei löschen.
  throw;
}
  Eingabedatei schließen. Ausgabedatei schließen.
}
Das wirkt ziemlich umständlich, und im Prinzip nur wie eine komplizierte 
Umformulierung der Verwendung von Rückgabewerten.
Was habe ich da falsch verstanden? Wie verwendet man Exceptions 
"sauber"?

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du muß natürlich WENN du das willst für jede Operation einen Block 
haben.
Z.B. wenn die Datei nicht gelesen werden kann könnte das mehrer Ursachen 
haben. Deshalb könnte man eine (generelle) Exeption haben die 
'DateiOpenException'... von dieser sind weitere abgeleitet:
DateiOpenException
-> ExistiertNichtException
-> SchreibgeschütztException
-> KeineZugrifsrechteException

Dein Möglichkeiten mit der "HauptException":
- Einfach sagen Öffnen geht nicht
- Auswahl bieten eine andere Datei zu öffnen
- an die nächst höhere Ebene "weiterwerfen"

Du könntest aber auch "ExistiertNichtException" abfangen und dann eine 
neue Datei anlegen und sonst die Exeption weitergeben... oder oder.

Die höhere Instanz kann dann entscheiden ob sie eine Möglichkeit hat den 
Fehler zu behandeln...

Zudem kannst du auch den "StackTrace" verfolgen um zu sehen wo das 
Problem auftrate... wenn die Funktion X dir als Rückgabewert nen Fehler 
meldet, weißt du nicht obs unterfunktion A oder B war... oder vieleicht 
ein Fehler den du noch garnicht bedacht hast. Man hat mit Exceptions 
also schon weitere Möglichkeiten...

Der große Vorteil ist einfach das du die Exception behandeln KANNST aber 
nicht MUßT.
int KonvertiereDatei(){
if (Eingabe-Datei öffnen != 0) {
 return 1;
}
if(Ausgabe-Datei öffnen != 0) {
 return 2;
}
if (Daten lesen != 0) {
 return 3;
}
if (Daten schreiben != 0) {
 return 4;
} 
return 0;
}
Ist auch nicht viel besser oder?

mit Exceptions könntest du aber machen:
void KonvertiereDatei () throws Dateifehler{
Eingabe-Datei öffnen;
Ausgabe-Datei öffnen;
Daten lesen;
Daten schreiben;
}

Wenn jezt in irgeneiner Funktion ein Dateifehler auftritt, könnte die 
übergeordnete Insztanz z.B. versuchen festzustellen das die Datei nicht 
gelesen werden konnte, und es dann nochmal mit einer anderen Datei 
probieren.

Das es danach "normal"/definiert weitergibt, darum kümmert sich "das 
System", der Programmierer muß dies nicht tun (also das keine 
korrupten/ungültigen Speicherinhalte entstehen).

Autor: Niklas Gürtler (erlkoenig)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Du könntest aber auch "ExistiertNichtException" abfangen und dann eine
> neue Datei anlegen und sonst die Exeption weitergeben... oder oder.
Das ginge mit Rückgabewert+errno auch, wäre aber vermutlich mehr 
Schreibarbeit.
> Die höhere Instanz kann dann entscheiden ob sie eine Möglichkeit hat den
> Fehler zu behandeln...
Wenn man überall konsequent Rückgabewerte+errno verwendet, sollte das 
wohl auch gehen, aber diese Weiterleitung würde wohl auch die Tastatur 
abnutzen.
> Zudem kannst du auch den "StackTrace" verfolgen um zu sehen wo das
> Problem auftrate...
Gut, das geht mit klassischen Methoden nicht, klarer Vorteil.
> Der große Vorteil ist einfach das du die Exception behandeln KANNST aber
> nicht MUßT.
Es ging mir jetzt um den Idealfall, wenn man alle Möglichkeiten 
behandeln will. Wenn man aber einen Rückgabewert ignoriert, dürfte das 
wahrscheinlich schwieriger aufzufinden sein als ein fehlendes 
try{}-catch{} (wo eigentlich eines hingehörte).
> Ist auch nicht viel besser oder?
Das es besser ist habe ich nicht behauptet :)
> mit Exceptions könntest du aber machen: ...
Okay, das wäre aber ziemlich "unsauber", weil dann der Aufrufer schwer 
sagen kann, was schiefgelaufen ist, und was jetzt mit den Dateien 
passiert ist. Wenn man das aber sowieso nicht beachten will, aber 
dennoch ein Fehler auftritt, ist er mit exceptions wohl leicher zu 
finden.
Vielen Dank, ich denke, jetzt habe ich den Sinn von Exceptions 
verstanden.
In einer perfekten Welt mit hyperinelligenten Programmierern braucht man 
sie wohl nicht, aber die haben wir ja nunmal nicht :)

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Exceptions sind aber ein bestimmter Fehler, eine Fehlernummer ist nur 
eine Zahl! z.B.

int x = log(0);

Was willst du als Fehlernummer zurückgeben?

Und auch der genannte Fall muß nicht unsauber sein! Wenn die Funktion 
z.B. eine Datei liest und die einen Wert zurückliefert willst du dich im 
Normalfall auf andere Funktionen zurückgreifen die ihrerseits eine 
Exception auslösen können. Natürlich KÖNNTEST du die jezt in deiner lese 
Routine verarbeiten... oder aber die Leseroutine geht erstmal davon aus 
das alles glatt läuft, und wenn nicht, wird der Fehler halt ggf von der 
aufrufenden Instanz behandelt (da du selber den Fehler vieleicht 
garnicht behandeln kannst).

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vorsicht, persönliche Meinung.

Exceptions sind genauso eine Seuche wie Fließkommazahlen: Jeder Depp 
will sie haben, in den allermeisten Fällen sind sie total unnötig und 
niemand macht sich mal die Mühe, zu verstehen, was sie überhaupt können 
und nicht können.

Man kann sie richtig benutzen und so weiter, keine Frage. Allzuoft aber 
artet das in faule Programmierung aus, so von wegen soll sich doch die 
nächste Instanz um den Fehler kümmern. Das haut voll gegen Kapselung 
etc.
Prinzipiell gibt es ja nur zwei Möglichkeiten: Entweder, ich vertusche 
Fehler (durch 'Abhauen') oder ich werfe sie nochmal. Kannst dir ja 
aussuchen, was besser oder sauberer ist.

Einen Backtrace krieg ich im Debugger auch ohne Exceptions hin, gar kein 
Problem.

Mit solchen Fällen wie z.B. 'log(0)' könnte man sich streiten, ob das 
entweder Aufgabe des FP-Prozessors ist, den Fehler aufzufangen (der hat 
ja auch ein Fehlerregister, welches man auslesen kann) oder ob es 
Aufgabe der Anwendung ist, falsche Werte erst garnicht so weit kommen zu 
lassen.

Die Sache mit dem Abbruch an der richtigen Stelle hast du ja selbst 
schon entdeckt: Erst throw, throw, throw und danach if, if, if, um 
festzustellen, bis wohin es geklappt hat, um danach sauber aufräumen zu 
können...

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu log(0) gibt es 3 Lösungen
-Exception
-global math_errno einführen
-interface ändern => double log(double val, int & err)

Aber das war ja nicht die Frage ;)

Wer hindert dich denn daran eine Exception Hierarchie aufzubauen?
Exception
   <- MemoryException
   <- MathException
   <- InvalidParameterException
   <- ..

dann kannst du

try {
   allocMem();
   double d = log(0);
   string name = getName("223");
}
catch(Exception & exp) {
   exp.what();
   exit(1);
}

Oder habe ich die Frage missverstanden?

Autor: Niklas Gürtler (erlkoenig)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> oder aber die Leseroutine geht erstmal davon aus dass alles glatt läuft
... aber wenn nicht, sind die Daten in einem inkonsistenten Status - das 
macht bei diesem Beispiel jetzt nicht so viel aus, aber wenn man eine 
Funktion hat, die mit komplexen Datenstrukturen im Speicher arbeitet und 
optimistisch davon ausgeht dass nichts schiefgeht, aber dann doch ein 
Fehler auftritt, können die Daten in einem halb-verarbeiteten Zustand 
sein, der jede weitere Verarbeitung unmöglich macht, was man durch 
abfangen der Exception auf höherer Ebene auch nicht mehr in den Griff 
kriegen kann, da die höhere Ebene ja gar nicht genau weiß, welche 
Operation fehlgeschlagen ist, um die Daten "reparieren" zu können.
> und niemand macht sich mal die Mühe, zu verstehen, was sie überhaupt
> können und nicht können.
Genau deswegen frage ich hier ja :)
> Einen Backtrace krieg ich im Debugger auch ohne Exceptions hin, gar kein
> Problem.
Aber dazu muss das Programm doch beim auftreten des Fehlers unterbrochen 
werden, damit du einen backtrace kriegst ... ? Wenn aber z.B. eine 
Funktion a () eine Funktion b () aufruft, in der fopen () aufgerufen 
wird, was fehlschlägt, sodass b() einen Fehler zurückgibt, sodass a () 
einen Fehler zurückgibt, dann weiß der Aufrufer von a () wohl, dass ein 
Fehler aufgetreten ist, aber dann kriegst du im Debugger doch keinen 
Backtrace? Um einen solchen zu kriegen, muss doch die Ausführung in 
einer Funktion (wie fopen) stehen bleiben (durch einen Breakpoint oder 
ein signal, SIGINT oder so), sodass man dann (z.B. in gdb) "bt" eingeben 
kann. Wenn aber fopen() mit Fehler zurückkehrt, genauso wie b() und a(), 
hast du doch keinen backtrace, oder übersehe ich da was?
> Was willst du als Fehlernummer zurückgeben?
> -interface ändern => double log(double val, int & err)
Zitat aus der log(x)-Manpage:
If x is zero [für nicht-Mathematiker: log(0) geht nicht], then a pole 
error occurs, and the functions return -HUGE_VAL [für die version mit 
double], -HUGE_VALF [für float], or -HUGE_VALL [für long double], 
respectively.
If x is negative (including negative infinity) [was auch eine ungültige 
Eingabe wäre], then a domain error occurs,  and  a  NaN  (not  a 
number)  is returned.
Ist doch gut, man kann ganz eindeutig unterscheiden, wann ein Fehler 
aufgetreten ist, und wann nicht, und was für ein Fehler es ist. Und das 
ohne Exceptions...

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja ich will hier keinen Glaubenskrieg auslösen... was ich angeführt 
habe waren nur BEISPIELE... und sicher gibt es n+1 Möglichkeiten das 
anders zu lösen keine Frage.
Ich glaube aber du denkst zu sehr in der 'C-Welt' wo man so manche 
Schweinerei als normalen Programmierstil empfindet ;)

Es sit einfach so, ein RÜckgabewert ist ein Rückgabewert, ein 
Fehler/Exception ist halt was völlig anderes.

Wenn du nen konkretes Beispiel/Problem hast könnt ich dir sicher dazu 
noch was erzählen ansosnten findest du bei Google sicher genug 
"Streitgespräche" in einschöägigen Foren :)

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Niklas G. wrote:
>> Einen Backtrace krieg ich im Debugger auch ohne Exceptions hin, gar kein
>> Problem.
> Aber dazu muss das Programm doch beim auftreten des Fehlers unterbrochen
> werden, damit du einen backtrace kriegst ... ?
Ist schon richtig, aber überlege mal: Was willst du zur Laufzeit mit 
einem Backtrace...?

>> Was willst du als Fehlernummer zurückgeben?
>> -interface ändern => double log(double val, int & err)
> Zitat aus der log(x)-Manpage:
> If x is zero [für nicht-Mathematiker: log(0) geht nicht], then a pole
> error occurs, and the functions return -HUGE_VAL [für die version mit
> double], -HUGE_VALF [für float], or -HUGE_VALL [für long double],
> respectively.
> If x is negative (including negative infinity) [was auch eine ungültige
> Eingabe wäre], then a domain error occurs,  and  a  NaN  (not  a
> number)  is returned.
> Ist doch gut, man kann ganz eindeutig unterscheiden, wann ein Fehler
> aufgetreten ist, und wann nicht, und was für ein Fehler es ist. Und das
> ohne Exceptions...

Und: 
http://www.gnu.org/software/libc/manual/html_node/...

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ist doch gut, man kann ganz eindeutig unterscheiden, wann ein
> Fehler aufgetreten ist, und wann nicht, und was für ein Fehler es ist.
> Und das ohne Exceptions...

Das geht halt nur für Funktionen, wo man spezielle Werte für Fehler 
reservieren kann. Die sind aber nicht für alle Funktionen gleich bzw. 
für manche Funktionen gibt es gar keine. Also würde es immer je nach 
Funktion unterschiedlich gehandhabt werden müssen.
Es gibt auch viele Programmierer, die es als schlecht ansehen, wenn 
Ergebnisse aus einer Funktion mit dem Fehlerhandling vermischt werden.

Autor: Niklas Gürtler (erlkoenig)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Es gibt auch viele Programmierer, die es als schlecht ansehen, wenn
> Ergebnisse aus einer Funktion mit dem Fehlerhandling vermischt werden.
Das macht Sinn. Man kann aber auch einer Funktion einen Pointer/Referenz 
übergeben, über den dann das Ergebnis ausgegeben wird, und der 
Rückgabewert zeigt einen Fehler oder Erfolg an, oder umgekehrt.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.