(Un-)Sinn von Exceptions

Algorithmen, Sprachunabhängige Diskussionen zu Konzepten, Programmiersprachen-Design
Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von Kerli » Fr Jul 25, 2008 10:57 pm

So, ist das Thema ja doch noch da ;)
Xin hat geschrieben:
Kerli hat geschrieben:Also mir fällt keine schöne Möglichkeit ohne Exceptions ein.
Wie willst Du eine Meinung vertreten, ob etwas besser ist, wenn Du aus mangelnden Wissen nichtmals keine Wahl hast?
Das mir keine bessere Möglichkeit einfällt heißt aber nicht, dass ich keine anderen Möglichkeiten kenne. Es ist meiner Meinung nach von allen Möglichkeiten, die ich bis jetzt gesehen habe die Schönste.
Xin hat geschrieben:

Code: Alles auswählen

DemoClass * demo;

demo = new (std::nothrow) DemoClass();
if( demo )
  if( demo->run() ) printf( "Success\n" );
  else              printf( "Failed\n" );
else                printf( "No Memory\n" );

delete demo;
Und was machst du wenn im Konstruktor von DemoClass auch mit 'new' Speicher angefordert wird? Wenn das fehlschlägt, dann bekommst du entweder in 'run()' einen Zugriffsfehler oder du musst für jeden angeforderten Speicher überprüfen ob er nicht null ist.
Xin hat geschrieben:Deine Schöne Lösung hat so einige Schönheitsfehler. Wenn Du eine bad_alloc bekommst, dann kannst Du nicht unterscheiden, ob Du nun das Objekt nicht erhalten hast, oder ob run() Mist gebaut hat.
Und wozu muss ich das wissen? Es geht mir in dem Fall nur darum ob ich für das Intanzieren der Klasse und das Aufrufen von 'run()' genug Speicher bekommen habe. Wo ich dann genau keinen Speicher mehr bekommen habe ist mir hier nicht wichtig.
Aber durch die Verwendung von Exceptions erspare ich mir direkt im Konstruktur und in 'run' jedes 'new' zu überprüfen.
Xin hat geschrieben:Füge das doch bitte mal hinzu, so dass Du das unterscheiden kannst. So sieht das ohne Exceptions aus.

Code: Alles auswählen

    DemoClass * demo;

    demo = new (std::nothrow) DemoClass();
    if( demo )
      if( demo->run() ) printf( "Success\n" );
      else              printf( "Failed\n" );
    else                printf( "No Memory\n" );

    delete demo;
Meine Version gibt übrigens den Speicher auch dann frei, wenn er Speicher bekommen hat... sie liefert viel genauere Informationen und sie ist schon jetzt kürzer als die Exceptions-Version.
Schreib mal Deine Exceptionsversion dazu und sag mir dann was Du persönlich als übersichtlicher empfindest.
Wenn man nur diesen Teil betrachtet, dann ist deine Lösung schöner, aber was machst du wenn in 'run' oder im Konstruktur ein 'new' fehlschlägt? In deiner Version müsstest du das jedesmal überprüfen. Bei mir brauch ich das nur einmal mit dem Abfangen von 'bad_alloc' behandeln.

Code: Alles auswählen

DemoClass *demo;
try
{
  demo = new DemoClass();
  demo->run();
  printf( "Success\n" );
}
catch(std::bad_alloc&)
{
  printf( "No Memory\n" );
}
catch(...)
{
  printf( "Failed\n" );
}
delete demo;
Und was machst du eigentlich wenn 'run' fehlschlägt? Wie kannst du dann festellen was fehlgeschlagen ist? Du kannst zwar zum Beispiel einen Integer als Rückgabetyp wählen und dann darüber verschieden Fehlercodes bekommen, aber sehr flexibel ist das nicht. Was ist wenn du einen neuen Fehlertyp hinzufügst? Dann müsstest du wieder einen neuen Fehlercode definieren, währenddessen ich mit Exceptions einfach einen Beschreibenden String übergeben kann und den ohne irgendeine Interpretation eines Fehlercodes direkt ausgeben oder verwenden kann.
Xin hat geschrieben:Okay, dann machen wir mal... Damit eine Exception kommen kann, muss sie geworfen werden, also muss sqrt() immer prüfen, ob ein positiver Wert übergeben wurde then rechnen else throw Exception.
Man kann auch einfach ganz normal die Wurzel berechnen und irgendwann würde man bei einer negativen Zahl auf einen Fehler stoßen bei der man eben statt den Fehlercode zurückzugeben eine Exception wirft. Außerdem wäre es auch kein wirklicher Geschwindigkeitsverlust eine Zahl auf negatives Vorzeichen zu testen, der eine Prozessortakt ist im Vergleich zur gesamten Laufzeit des Wurzelalgorithmus vernachlässigbar klein.
Xin hat geschrieben: Nehmen wir mal ein Beispiel, wo es nicht so um Effizients ankommt, zum Beispiel 3D Berechnungen. Da nehmen wir einen 3D Vector und wollen die Länge wissen. Für unsere Kleineren und die, denen die Vektorrechnung grade abhanden gekommen ist:
Vektorlänge = sqrt( x*x + y*y + z*z );
Multipliziert man zwei positive Zahlen, kommt was positives raus, sind beide Zahlen negativ kommt was positives raus. Addiert man positive Zahlen, kommt auch was positives raus. In diesem Algorithmus kann und da kannst Du Dich auf den Kopf stellen, es kann NIEMALS eine negative Zahl in sqrt() reingehen.
Wenn keine negative Zahlen kommen, dann kann auch kein Fehler auftreten, und es wird deshalb auch keine Zeit mit dem Werfen einer Exception verbraucht.
Xin hat geschrieben:und in Java schreibst Du zusätzlich noch zwangsweise eine Fehlerbehandlung für eine Fehler, der - ich erwähnte es bereits - NIEMALS kommen kann.
Hab ich schon irgendwann einmal erwähnt, dass ich Java nicht mag :)
Xin hat geschrieben:Du hast keine Ahnung, woher bad_alloc bei Dir im Beispiel fliegt, Du befindest Dich in Deinem catch Block absolut im Blindflug und nennst das auch noch eine schöne Lösung.
In meinem Beispiel interessiert es mich auch nicht welchen Speicher ich nicht bekommen habe, sondern nur ob ich einen nicht bekommen habe, so dass ich nachher alle bereits erhaltenen Speicherbereiche wieder freigeben kann. Ohne Exceptions müsste ich dafür nach jeder Speicheranforderung überprüfen ob sie funktioniert hat.
Xin hat geschrieben:Die Haupterrungenschaft, die Exception geleistet haben ist dass das Programm nicht einfach so abschmiert, sondern vorher ein PopUp-Fenster aufgeht, dass erklärt, dass das Programm abgeschmiert ist, meine Daten im Arsch sind - tut uns leid - und ich das Programm mit 'Ok' nun beenden kann.
Unter'm Strich ist genau das die Leistung, die Exceptions bringen. Eine Abnahme der Fehlerbehandlung, weil sie zu aufwendig wird und eine absolut zynische Meldung an den Benutzer.
Das ist wieder die Verantwortung des Programmierers was er damit macht. Wenn man zum Beispiel keinen Speicher mehr bekommt, dann kann man ja trotzdem noch die wichtigen Daten speichern und erst dann das Programm beenden.
Xin hat geschrieben:Exceptions sind Ausnahmen, aber sie werden nicht in Ausnahmen verwendet, ihre Verwendung ist - grade in Java - die Regel. Und da hakts.
Java ist auch nicht unbedingt eine schöne Programmiersprache...
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von Xin » Fr Jul 25, 2008 11:46 pm

Kerli hat geschrieben:Und was machst du wenn im Konstruktor von DemoClass auch mit 'new' Speicher angefordert wird? Wenn das fehlschlägt, dann bekommst du entweder in 'run()' einen Zugriffsfehler oder du musst für jeden angeforderten Speicher überprüfen ob er nicht null ist.
Hier kommen wir nun langsam in Bereiche, wo C++ sprachliche Möglichkeiten ausgehen.
Soweit ich weiß fängt new( std:nothrow ) die Exception ab und liefert dafür NULL.

Kein valides Objekt => NULL.
Xin hat geschrieben:Deine Schöne Lösung hat so einige Schönheitsfehler. Wenn Du eine bad_alloc bekommst, dann kannst Du nicht unterscheiden, ob Du nun das Objekt nicht erhalten hast, oder ob run() Mist gebaut hat.
Und wozu muss ich das wissen? Es geht mir in dem Fall nur darum ob ich für das Intanzieren der Klasse und das Aufrufen von 'run()' genug Speicher bekommen habe. Wo ich dann genau keinen Speicher mehr bekommen habe ist mir hier nicht wichtig.[/quote]
Hmm... das könnte darüber entscheiden, ob Du unberührte Daten hast, oder Daten, die in einem undefinierten Zustand sind. Die Exception wäre die gleiche, entscheident ist halt, ob sie bei new oder bei run() auftritt.
Kerli hat geschrieben:Aber durch die Verwendung von Exceptions erspare ich mir direkt im Konstruktur und in 'run' jedes 'new' zu überprüfen.
Stimmt. statt if(), try {} catch catch catch catch...
Kerli hat geschrieben:Wenn man nur diesen Teil betrachtet, dann ist deine Lösung schöner, aber was machst du wenn in 'run' oder im Konstruktur ein 'new' fehlschlägt? In deiner Version müsstest du das jedesmal überprüfen. Bei mir brauch ich das nur einmal mit dem Abfangen von 'bad_alloc' behandeln.
Jow... nur weißt Du halt nicht, was Du behandelst oder behandeln kannst. Blindflug bedeutet Radikalkur.
Wenn new Objekt nun 50GB bewegt, weil eine Anfrage auf den Datensatz ein merkwürdiges Ergebnis (Exception) liefert, dann mal gut Nacht.
Kerli hat geschrieben:

Code: Alles auswählen

DemoClass *demo;
try
{
  demo = new DemoClass();
  demo->run();
  printf( "Success\n" );
}
catch(std::bad_alloc&)
{
  printf( "No Memory\n" );
}
catch(...)
{
  printf( "Failed\n" );
}
delete demo;
Und was machst du eigentlich wenn 'run' fehlschlägt?
Eben... die Frage stelle ich Dir ja auch andauernd und trotzdem baust Du hier noch eine Lösung zusammen, die genau diese Frage auch nicht beantwortet. Schade eigentlich.
Kerli hat geschrieben:Wie kannst du dann festellen was fehlgeschlagen ist? Du kannst zwar zum Beispiel einen Integer als Rückgabetyp wählen und dann darüber verschieden Fehlercodes bekommen, aber sehr flexibel ist das nicht. Was ist wenn du einen neuen Fehlertyp hinzufügst? Dann müsstest du wieder einen neuen Fehlercode definieren, währenddessen ich mit Exceptions einfach einen Beschreibenden String übergeben kann und den ohne irgendeine Interpretation eines Fehlercodes direkt ausgeben oder verwenden kann.
Sekunde... Du wirst Exceptions (AUSNAHMEN), um Strings weiterzuleiten... dafür brauche ich nur ein char * zurückzugeben.
Sorry, noch verfehlter kann eine Exception kaum verwendet werden.
Kerli hat geschrieben:
Xin hat geschrieben:Okay, dann machen wir mal... Damit eine Exception kommen kann, muss sie geworfen werden, also muss sqrt() immer prüfen, ob ein positiver Wert übergeben wurde then rechnen else throw Exception.
Man kann auch einfach ganz normal die Wurzel berechnen und irgendwann würde man bei einer negativen Zahl auf einen Fehler stoßen bei der man eben statt den Fehlercode zurückzugeben eine Exception wirft. Außerdem wäre es auch kein wirklicher Geschwindigkeitsverlust eine Zahl auf negatives Vorzeichen zu testen, der eine Prozessortakt ist im Vergleich zur gesamten Laufzeit des Wurzelalgorithmus vernachlässigbar klein.
Man stößt auf keinen Fehler, man gibt eben nur Unsinn zurück.
Und das 'der eine Prozessortakt'-Blablabla, macht viel aus, wenn er in grundlegenden Funktionen liegt. Dann ist es eben nicht nur der eine, sondern der eine, der Millionenfach überflüssig verbraten wird. Und wer mit der Einstellung 'der eine Prozessortakt' an die Sache rangeht, der verbraucht in jeder Funktion noch einen Prozessortakt. Und dazu kommen die Prozessortakte, die try verbrät und die Prozessortakte, die die '}' am Ende von Try verbrät und das in jeder try-Verschachtelung. Und so ist man selbst bei effizienter Programmierung eben nicht mehr bei nur dem einen Takt sondern bei tausenden und wem das egal ist, der achtet auch auf viele andere Dinge nicht.
Ein Algorithmus, der bei mir eine Minute lief, habe ich mit Optimierungen, die sich ausschließlich um Dinge, wie 'diesen einen Takt' drehten auf 23 Sekunden gebracht. Anders ausgedrückt: über 50% der Zeit verbrachte der Prozessor damit Dinge zu tun, die nichts mit der Rechnung zu tun hatten.

Der Algorithmus war von mir und so schlecht war der gar nicht geschrieben. Da es hier aber darum ging schnell zu sein, weil dieser Algorithmus tausende Male gerufen wird, lohnte sich da wirklich jeder Takt.
Kerli hat geschrieben:
Xin hat geschrieben: Nehmen wir mal ein Beispiel, wo es nicht so um Effizients ankommt, zum Beispiel 3D Berechnungen. Da nehmen wir einen 3D Vector und wollen die Länge wissen. Für unsere Kleineren und die, denen die Vektorrechnung grade abhanden gekommen ist:
Vektorlänge = sqrt( x*x + y*y + z*z );
Multipliziert man zwei positive Zahlen, kommt was positives raus, sind beide Zahlen negativ kommt was positives raus. Addiert man positive Zahlen, kommt auch was positives raus. In diesem Algorithmus kann und da kannst Du Dich auf den Kopf stellen, es kann NIEMALS eine negative Zahl in sqrt() reingehen.
Wenn keine negative Zahlen kommen, dann kann auch kein Fehler auftreten, und es wird deshalb auch keine Zeit mit dem Werfen einer Exception verbraucht.
Aber mit der Abfrage und dem Einrichten der Möglichkeit, dass eine Exception kommen könnte und dem Entfernen der Möglichkeit, wenn der Block durchlaufen wird.
Kerli hat geschrieben:
Xin hat geschrieben:Du hast keine Ahnung, woher bad_alloc bei Dir im Beispiel fliegt, Du befindest Dich in Deinem catch Block absolut im Blindflug und nennst das auch noch eine schöne Lösung.
In meinem Beispiel interessiert es mich auch nicht welchen Speicher ich nicht bekommen habe, sondern nur ob ich einen nicht bekommen habe, so dass ich nachher alle bereits erhaltenen Speicherbereiche wieder freigeben kann. Ohne Exceptions müsste ich dafür nach jeder Speicheranforderung überprüfen ob sie funktioniert hat.
Das tut man auch mit Exceptions, die Exeptions fliegen nämlich nicht einfach so - es ist nur teurer.
Kerli hat geschrieben:Das ist wieder die Verantwortung des Programmierers was er damit macht. Wenn man zum Beispiel keinen Speicher mehr bekommt, dann kann man ja trotzdem noch die wichtigen Daten speichern und erst dann das Programm beenden.
Könnte er, wenn er wüßte, was schief gegangen ist. Wie willst Du denn Daten speichern, Du weißt ja nichtmals ob Du überhaupt ein Objekt hast.
Kerli hat geschrieben:
Xin hat geschrieben:Exceptions sind Ausnahmen, aber sie werden nicht in Ausnahmen verwendet, ihre Verwendung ist - grade in Java - die Regel. Und da hakts.
Java ist auch nicht unbedingt eine schöne Programmiersprache...
C++ ist hier nicht schöner. Nur benutzt man Exceptions deutlich weniger.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von fat-lobyte » Sa Jul 26, 2008 6:06 am

Hier mal meine Meinung zu dem Thema:
Es stimmt, Exceptions kosten laufzeit. In Zeitkritischem Code haben sie wirklich nichts zu suchen, und insofern war das Beispiel von sqrt() nicht wirklich gut gewählt.
Es stimmt auch, dass man sich im endeffekt manchmal durch einen try- catch- block Wald durchkämpfen muss.
Das ist aber bei Rückgabewerten nicht anders! Ob ich mich bei Exceptions nicht auskenne, oder bei Rückgabewerten ist ziemlich egal.

Fakt ist, ich kann mit Exceptions viel mehr Informationen übertragen. Du kannst diese Informationen natürlich Ignorieren, wenns dir Spaß macht. Wenn nicht, dann ignorierst du die infos aber nicht.

Fakt ist auch, Exceptions bringen eine neue art von Flow control möglichkeiten in die Sprache. Ich muss nicht über 10 funktionsaufrufe zurückgehen, in jedem Frame die Ressourcen Freigeben, und in jedem Frame meinen Senf dazugeben. Ich schmeisse _eine_ Exception, und die sache ist erledigt.
std::fstream hat einen Destruktor, und dieser schließt die Datei automatisch. Zu deinen mit new allokierten dingen:
Es gibt boost::scoped_ptr, es gibt std::tr1::scoped_ptr, und es wird in einem Jahr std::scoped_ptr geben. Vorstellen kannst du dir das ca. so:

Code: Alles auswählen

template <typename T>
class scoped_ptr
{
    T*     internal_ptr;

public:
    scoped_ptr()
        internal_ptr(NULL)
    {}

    scoped_ptr(T* ptr)
        internal_ptr(ptr)
    {}

    // korrekte assignment und copy constructors

    ~scoped_ptr(T* ptr)
    {
        delete ptr;
    }
    
    T& operator *()
    {
         // ...
    }

    whatever& operator ->()
    {
         // ...
    }
}

Du erstellst ein objekt auf dem heap, steckst es sofort da rein, und bei der nächsten exception wird es sofort gelöscht.

Ich will nicht wissen, wie ich das anders programmieren würde. Mich interessierts auch nicht. Ich habe schon einmal gesagt: ich brauche keine Krücken um mir Features nachzubauen, die schon vorhanden sind. Exceptions sind sehr oft die _einfachste_ und die _beste_ methode, Fehler zu propagieren, ganz besonders wenns um Ressourcen ankommt.

Ich will nicht 100 mal abfragen ob .is_good() oder .is_open() oder .succeeded() oder sonstwas. ICh schreibe ein throw und ein Catch, und die Sache hat sich erledigt.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von Xin » Sa Jul 26, 2008 11:15 am

fat-lobyte hat geschrieben:Es stimmt auch, dass man sich im endeffekt manchmal durch einen try- catch- block Wald durchkämpfen muss.
Das ist aber bei Rückgabewerten nicht anders! Ob ich mich bei Exceptions nicht auskenne, oder bei Rückgabewerten ist ziemlich egal.
Egal ist es nicht, denn die direkte Kontrolle, den Zwang genau da zu prüfen, wo die Funktion gerufen wird, klärt ohne irgendwelche Zweifel, wer für die Fehlerbehandlung zuständig ist.
Fehlerbehandlung ist lästige Arbeit. Arbeit wird gerne weitergereicht und throw macht genau das.
Konzepte von Programmiersprachen müssen auch darauf eingehen, dass nur auf der einen Seite Rechensklaven sitzen - auf der anderen Seite sitzen Menschen und die haben nicht die Eigenschaft, bei lästigen Arbeiten "Hier!" zu rufen, sondern sie weiterzureichen. Konkret jemanden zu haben, der verantwortlich ist, ist wichtig. Man muss mit dem Finger auf eine Person zeigen können und sagen können 'Du hast Scheiße gebaut' und dann wird diese Person beim nächsten Mal versuchen, es besser zu machen.
Eine Exception kracht durch das komplette Programm und dann trifft man sich im Gruppenraum und sagt 'Ihr alle habt Scheiße gebaut' und jeder denkt für sich, 'Joah, das sagt der andauernd, aber mir hat er noch nie was nachgewiesen'.
fat-lobyte hat geschrieben:Fakt ist, ich kann mit Exceptions viel mehr Informationen übertragen.
Welche denn? Mal eben ein "Fakt" in den Raum stellen ist ja schön und gut, aber welche Mehr-Information überträgst Du denn?
fat-lobyte hat geschrieben:Fakt ist auch, Exceptions bringen eine neue art von Flow control möglichkeiten in die Sprache. Ich muss nicht über 10 funktionsaufrufe zurückgehen, in jedem Frame die Ressourcen Freigeben, und in jedem Frame meinen Senf dazugeben. Ich schmeisse _eine_ Exception, und die sache ist erledigt.
Wusstest Du, dass schmeißen und scheißen ursprünglich aus einem Wort entstanden sind, dass (braunen) Dreck werfen beschrieb? Lehmhausbau: man warf braunen Lehm an die Wand.
Die Verwandtschaft hört man ihnen heute noch an und in meinen Programmen werden keine Exceptions geschissen. Aber Deine Wortwahl geht mit meiner Meinung überein: Exceptions sind Analprodukte.

Dass Du in jedem Frame Deinen Senf dazugeben kannst ist schön... und wer interessiert sich dafür?
Wichtig ist die Information, ob ich ein Ergebnis bekam oder nicht. Und das interessiert nur im Frame über der Funktion, die fehlschlug.
fat-lobyte hat geschrieben:Es gibt boost::scoped_ptr, es gibt std::tr1::scoped_ptr, und es wird in einem Jahr std::scoped_ptr geben.
Die ScopePointer, AutoPointer usw. gibt's überall...
fat-lobyte hat geschrieben:Ich habe schon einmal gesagt: ich brauche keine Krücken um mir Features nachzubauen, die schon vorhanden sind.
...und auch hier nutzt Du die richtige Wortwahl: Krücken, die schon vorhanden sind.
Auch Krücken, die schon vorhanden sind, sind Krücken.
fat-lobyte hat geschrieben:Ich will nicht 100 mal abfragen ob .is_good() oder .is_open() oder .succeeded() oder sonstwas. ICh schreibe ein throw und ein Catch, und die Sache hat sich erledigt.
Du lässt immer nachfragen und zwar innerhalb der Funktionen, weil anders kann es C++ nicht.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von fat-lobyte » Sa Jul 26, 2008 3:20 pm

Xin hat geschrieben:Egal ist es nicht, denn die direkte Kontrolle, den Zwang genau da zu prüfen, wo die Funktion gerufen wird, klärt ohne irgendwelche Zweifel, wer für die Fehlerbehandlung zuständig ist.
Fehlerbehandlung ist lästige Arbeit. Arbeit wird gerne weitergereicht und throw macht genau das.
Konzepte von Programmiersprachen müssen auch darauf eingehen, dass nur auf der einen Seite Rechensklaven sitzen - auf der anderen Seite sitzen Menschen und die haben nicht die Eigenschaft, bei lästigen Arbeiten "Hier!" zu rufen, sondern sie weiterzureichen.
Tust du bei Rückgabewerten etwa nicht das gleiche? Du Siehst, dass ein Funktion einen Fehler zurückgibt und was machst du? Du gibts den Fehler erst wieder an die übergeordnete Funktion weiter. Ich verstehe einfach nicht, was daran so schwierig ist die Exception abzufangen. Wo ist der unterschied zwischen "return E_SOMETHING;" und "throw MyException("Something");" ? Du kannst die exception genauso ignorieren, dann tust du sie in dein geliebtes catch(const E& e) {}, und das wars. Wenn du ihn nicht ignorierst, kannst du ihn entweder abfangen, verändern und weiterwerfen. Du kannst ihn aber auch gleich durchwerfen, so dass der Fehler an die übergeordnete Funktion abgegeben wird. Aber das ist genau das gleiche was du mit Fehlercodes auch machen würdest.
Xin hat geschrieben:Konkret jemanden zu haben, der verantwortlich ist, ist wichtig. Man muss mit dem Finger auf eine Person zeigen können und sagen können 'Du hast Scheiße gebaut' und dann wird diese Person beim nächsten Mal versuchen, es besser zu machen.
Eine Exception kracht durch das komplette Programm und dann trifft man sich im Gruppenraum und sagt 'Ihr alle habt Scheiße gebaut' und jeder denkt für sich, 'Joah, das sagt der andauernd, aber mir hat er noch nie was nachgewiesen'.
Es gibt einen verantwortlichen. Der Verantwortliche ist der, der die Funktion aufruft, die die Exception werfen könnte. Wenn er sie nicht abfängt, heißt es 'Du hast Scheiße gebaut' und nicht 'Ihr alle habt Scheiße gebaut'.
Außerdem: aus welchem Grund sollte die Exception durchkrachen? Wenn die Exception durchkracht ist das ein _bug_, der behoben werden muss. Mit normalem Laufzeitverhalten (wie z.B. Fehlgeschlagenes Dateiöffnen, oder sonstwas) hat das gar nichts mehr zu tun. Eine durchkrachende Exception kannst du dir vorstellen, wie ein Segmentation Fault, nur mit dem Unterschied dass du beim SIGSEGV meist keinen Tau mehr hast, wo der Wirkliche Fehler war.
Xin hat geschrieben:
fat-lobyte hat geschrieben:Fakt ist, ich kann mit Exceptions viel mehr Informationen übertragen.
Welche denn? Mal eben ein "Fakt" in den Raum stellen ist ja schön und gut, aber welche Mehr-Information überträgst Du denn?
Typ, Membervariablen, private membervariablen, Methoden, die für die Fehlerbehebung nützlich sein könnten.
Xin hat geschrieben:
fat-lobyte hat geschrieben:Fakt ist auch, Exceptions bringen eine neue art von Flow control möglichkeiten in die Sprache. Ich muss nicht über 10 funktionsaufrufe zurückgehen, in jedem Frame die Ressourcen Freigeben, und in jedem Frame meinen Senf dazugeben. Ich schmeisse _eine_ Exception, und die sache ist erledigt.
Wusstest Du, dass schmeißen und scheißen ursprünglich aus einem Wort entstanden sind, dass (braunen) Dreck werfen beschrieb? Lehmhausbau: man warf braunen Lehm an die Wand.
Die Verwandtschaft hört man ihnen heute noch an und in meinen Programmen werden keine Exceptions geschissen. Aber Deine Wortwahl geht mit meiner Meinung überein: Exceptions sind Analprodukte.
/* No comment. */
Xin hat geschrieben:Dass Du in jedem Frame Deinen Senf dazugeben kannst ist schön... und wer interessiert sich dafür?
Wichtig ist die Information, ob ich ein Ergebnis bekam oder nicht. Und das interessiert nur im Frame über der Funktion, die fehlschlug.
Ich verstehe den einwand nicht. Wenn dich das Frame nicht interessiert, dann lass es. Lass die Exception durchrasseln bis zur nächsten Funktion und Sense. Wie gesagt, du kannst die exception abfangen wo du willst.

Xin hat geschrieben:
fat-lobyte hat geschrieben:Es gibt boost::scoped_ptr, es gibt std::tr1::scoped_ptr, und es wird in einem Jahr std::scoped_ptr geben.
Die ScopePointer, AutoPointer usw. gibt's überall...
Na eben, wo ist dann das Problem mit dynamischem Speicher?
Xin hat geschrieben:
fat-lobyte hat geschrieben:Ich habe schon einmal gesagt: ich brauche keine Krücken um mir Features nachzubauen, die schon vorhanden sind.
...und auch hier nutzt Du die richtige Wortwahl: Krücken, die schon vorhanden sind.
Auch Krücken, die schon vorhanden sind, sind Krücken.
Schön, dass du meine Worte so genau untersuchst. Was ich eigentlich meine weißt du wahrscheinlich genau: Wenn ich mir Krücken baue, um ein Ziel zu erreichen, das man mit vollwertigen Sprachfeatures viel leichter und sicherer erreichen könnte, dann läuft etwas falsch.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von Xin » Sa Jul 26, 2008 4:18 pm

fat-lobyte hat geschrieben:
Xin hat geschrieben:Egal ist es nicht, denn die direkte Kontrolle, den Zwang genau da zu prüfen, wo die Funktion gerufen wird, klärt ohne irgendwelche Zweifel, wer für die Fehlerbehandlung zuständig ist.
Fehlerbehandlung ist lästige Arbeit. Arbeit wird gerne weitergereicht und throw macht genau das.
Konzepte von Programmiersprachen müssen auch darauf eingehen, dass nur auf der einen Seite Rechensklaven sitzen - auf der anderen Seite sitzen Menschen und die haben nicht die Eigenschaft, bei lästigen Arbeiten "Hier!" zu rufen, sondern sie weiterzureichen.
Tust du bei Rückgabewerten etwa nicht das gleiche? Du Siehst, dass ein Funktion einen Fehler zurückgibt und was machst du? Du gibts den Fehler erst wieder an die übergeordnete Funktion weiter. Ich verstehe einfach nicht, was daran so schwierig ist die Exception abzufangen. Wo ist der unterschied zwischen "return E_SOMETHING;" und "throw MyException("Something");" ? Du kannst die exception genauso ignorieren, dann tust du sie in dein geliebtes catch(const E& e) {}, und das wars. Wenn du ihn nicht ignorierst, kannst du ihn entweder abfangen, verändern und weiterwerfen. Du kannst ihn aber auch gleich durchwerfen, so dass der Fehler an die übergeordnete Funktion abgegeben wird. Aber das ist genau das gleiche was du mit Fehlercodes auch machen würdest.
Ist es das?
Ein E_SOMETHING ist meist gar nicht notwendig, meistens reicht ein bool als Rückgabe, das angibt, ob die Aktion erfolgreich war oder nicht. Wie bei bei sqrt() kann man häufig auch den Algorithmus mit falschen Werten einfach durchlaufen lassen, die Abfrage steht sowieso an der falschen Stelle, denn der Bug passiert viel früher. Man behebt den Bug nicht, ein Assert, das an der richtigen Stelle Erwartungen abklopft hilft mehr und kostet später nichts mehr.

Der Aufwand ein E_SOMETHING zu deklarieren ist wesentlich geringer als für alles eine eigene Exception zu erzeugen. Die Leute sind wesentlich eher bereit, eine Zeile mit einem 'const int' zu definieren als eine neue Klasse. Die Idee hinter den Exceptions basiert auf dem Datentyp der Exception. Damit das ganze nämlich sauber und unterscheidbar bleibt, braucht jede Möglichkeit, eine Datei nicht zu finden, ihr eigene FileNotFoundException. Weicht man das ganze wieder auf, in dem man FileNotFoundException( "Preferences" ) oder FileNotFoundException( "DataFile" ) wirft, ist der Catch-Block für FileNotFoundException ein Alibi, aber keine Fehlerbehandlung, wenn eine FileNotFoundException( "swapfile" ) vorbeifliegt.
fat-lobyte hat geschrieben:
Xin hat geschrieben:Konkret jemanden zu haben, der verantwortlich ist, ist wichtig. Man muss mit dem Finger auf eine Person zeigen können und sagen können 'Du hast Scheiße gebaut' und dann wird diese Person beim nächsten Mal versuchen, es besser zu machen.
Eine Exception kracht durch das komplette Programm und dann trifft man sich im Gruppenraum und sagt 'Ihr alle habt Scheiße gebaut' und jeder denkt für sich, 'Joah, das sagt der andauernd, aber mir hat er noch nie was nachgewiesen'.
Es gibt einen verantwortlichen. Der Verantwortliche ist der, der die Funktion aufruft, die die Exception werfen könnte. Wenn er sie nicht abfängt, heißt es 'Du hast Scheiße gebaut' und nicht 'Ihr alle habt Scheiße gebaut'.
Außerdem: aus welchem Grund sollte die Exception durchkrachen? Wenn die Exception durchkracht ist das ein _bug_, der behoben werden muss. Mit normalem Laufzeitverhalten (wie z.B. Fehlgeschlagenes Dateiöffnen, oder sonstwas) hat das gar nichts mehr zu tun. Eine durchkrachende Exception kannst du dir vorstellen, wie ein Segmentation Fault, nur mit dem Unterschied dass du beim SIGSEGV meist keinen Tau mehr hast, wo der Wirkliche Fehler war.
Gelegentlich benutze ich absichtliche SigFaults als "Break"-Point, um dann zu gucken, wie grade die Variablen stehen.

Die Verantwortung schwindet, die meisten benutzen eine Exception, die sie irgendwo finden, frickeln sie was um, bauen einen merkwürdigen Konstruktor dazu und dann wird eine NameDerFirmaException geworfen, es landet alles in einem catch-Block, der anschließend total überfordert ist und kein Entwickler schafft es, den Müll wieder auseinanderzufrickeln.

Nix für ungut, aber das sind die Realitäten, wenn es eben nicht nur ein Übungsprogramm ist. Die Fehlerbehandlung verliert sich in allen Quelltexten. Ein Rückgabewert reicht bis zur nächsten Ebene und da findet die Fehlerbehandlung statt - oder nicht, aber dann weiß man zumindest, wo sie nicht stattfindet.

Und wie Du selbst sagst: Wenn die Exception durchkracht ist es ein Bug. Wenn sie sich verheddert ist es auch ein Bug, aber man sieht ihn nicht. Weißt Du wie oft Exceptions abgefangen werden, obwohl man dieses Verhalten nicht wünschte? Weil irgendeine Oberklasse von einem Programmierer eingefangen irgendwo wird, dem gar nicht bewußt ist, was da alles vorbeifliegen kann? Der behandelt den Fehler, das Programm bleibt in einem undefinierten Zustand und dann gucken wir mal, wie lange es noch läuft.
Ein Segmentation Fault ist mir da lieber, denn der ist ziemlich gut sichtbar und deutlich näher am Fehler, als eine durchgeknallte Exception, die zustande kam, weil andere Exceptions irgendwo versehentlich abgefangen wurden.
Xin hat geschrieben:
fat-lobyte hat geschrieben:Fakt ist, ich kann mit Exceptions viel mehr Informationen übertragen.
Welche denn? Mal eben ein "Fakt" in den Raum stellen ist ja schön und gut, aber welche Mehr-Information überträgst Du denn?
Typ, Membervariablen, private membervariablen, Methoden, die für die Fehlerbehebung nützlich sein könnten. [/quote]
Wenn ich das wirklich brauche, sehe ich grade nicht das Problem, das ebenfalls zurückzugeben.
fat-lobyte hat geschrieben:
Xin hat geschrieben:Dass Du in jedem Frame Deinen Senf dazugeben kannst ist schön... und wer interessiert sich dafür?
Wichtig ist die Information, ob ich ein Ergebnis bekam oder nicht. Und das interessiert nur im Frame über der Funktion, die fehlschlug.
Ich verstehe den einwand nicht. Wenn dich das Frame nicht interessiert, dann lass es. Lass die Exception durchrasseln bis zur nächsten Funktion und Sense. Wie gesagt, du kannst die exception abfangen wo du willst.
"Du kannst"... es macht aber keiner... und schonmal gar nicht, wenn "Du" nicht für den Bereich verantwortlich bist, der die Exception *DEINER MEINUNG* nach abfangen sollte. Der Entwickler soll eine lästige Aufgabe erfüllen und Deine Fehlerbehandlung machen? Es ist wahrscheinlich, dass der der Meinung ist, dass Du die Fehlerbehandlung selbst machen sollst, oder er weiß gar nicht so genau, was Du da eigentlich willst, jetzt kommt da eine Exception an mit der er nix anfangen will und eigentlich muss er sich um ganz andere Dinge kümmern - also was tun. throw away. Problem gelöst.
fat-lobyte hat geschrieben:
Xin hat geschrieben:
fat-lobyte hat geschrieben:Es gibt boost::scoped_ptr, es gibt std::tr1::scoped_ptr, und es wird in einem Jahr std::scoped_ptr geben.
Die ScopePointer, AutoPointer usw. gibt's überall...
Na eben, wo ist dann das Problem mit dynamischem Speicher?
Diese Form von Auto-Pointern ist eine Krücke, wie Du sie nennst. Und es ist eine Krücke.
fat-lobyte hat geschrieben:Was ich eigentlich meine weißt du wahrscheinlich genau: Wenn ich mir Krücken baue, um ein Ziel zu erreichen, das man mit vollwertigen Sprachfeatures viel leichter und sicherer erreichen könnte, dann läuft etwas falsch.
Du benutzt AutoPointer-Krücken, weil Sprachfeatures fehlen.
Es läuft etwas falsch.

AutoPointer sind eine nette Idee, finde ich wirklich, aber hier wird gefrickelt, damit Exceptions funktionieren.
Ähnliches der finally-Block: Da stürzt man im Blindflug rein, keine Ahnung, in welchem Zustand das Objekt grade ist, weil die Statusinformationen, die man zwangsläufig besitzt, wenn man Funktion für Funktion abprüfend abarbeited, erst mühevoll rekonstruiert werden müssen.
Hier wird Fehlerbehandlung nicht nur lästig, hier wird sie auch sehr aufwendig und vieles lässt sich einfach mit einem weiteren throw beiseite wischen zu jemanden, der nicht weiß, was er damit anfangen soll.


Nochmals: Ich bin nicht gegen Exceptions, aber ich bin absolut gegen die Verwendung von Exceptions als Regel. Dass Dateien nicht gefunden werden ist keine Ausnahme.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von fat-lobyte » Sa Jul 26, 2008 5:56 pm

Xin hat geschrieben:
fat-lobyte hat geschrieben:Tust du bei Rückgabewerten etwa nicht das gleiche? Du Siehst, dass ein Funktion einen Fehler zurückgibt und was machst du? Du gibts den Fehler erst wieder an die übergeordnete Funktion weiter. Ich verstehe einfach nicht, was daran so schwierig ist die Exception abzufangen. Wo ist der unterschied zwischen "return E_SOMETHING;" und "throw MyException("Something");" ? Du kannst die exception genauso ignorieren, dann tust du sie in dein geliebtes catch(const E& e) {}, und das wars. Wenn du ihn nicht ignorierst, kannst du ihn entweder abfangen, verändern und weiterwerfen. Du kannst ihn aber auch gleich durchwerfen, so dass der Fehler an die übergeordnete Funktion abgegeben wird. Aber das ist genau das gleiche was du mit Fehlercodes auch machen würdest.
Ist es das?
Ein E_SOMETHING ist meist gar nicht notwendig, meistens reicht ein bool als Rückgabe, das angibt, ob die Aktion erfolgreich war oder nicht. Wie bei bei sqrt() kann man häufig auch den Algorithmus mit falschen Werten einfach durchlaufen lassen, die Abfrage steht sowieso an der falschen Stelle, denn der Bug passiert viel früher. Man behebt den Bug nicht, ein Assert, das an der richtigen Stelle Erwartungen abklopft hilft mehr und kostet später nichts mehr.
Du kannst übrigens auch Exceptions von typ bool werfen, wenns dir Spaß macht - nur so nebenbei. sqrt() ist wirklich ein ziemlich schlechtes Beispiel, da in solchen Fällen (zeitkritische Funktionen) ein assert() nicht schaden kann.
Xin hat geschrieben:Der Aufwand ein E_SOMETHING zu deklarieren ist wesentlich geringer als für alles eine eigene Exception zu erzeugen.
Der aufwand in jeder Funktion die Fehlschlagen kann einen Zeiger oder Referenz als Argument zu übergeben, weil der Rückgabewert nicht verwendet werden kann ist wesentlich höher als für eine Art von Fehler einen Typen zu deklarieren und ihn dann mehrmals zu verwenden.
Xin hat geschrieben:Die Leute sind wesentlich eher bereit, eine Zeile mit einem 'const int' zu definieren als eine neue Klasse.
Wer sind "Die Leute"? Sprichst du für dich, hast du recht. Ich zum Beispiel habe kein Problem eine Klasse zu definieren, um sie dann als Exception zu verwenden.

Code: Alles auswählen

struct FileError 
{
    enum {FILENOTFOUND = 0} reason;
    std::string msg;
}
Das war nicht allzu lang oder?
Xin hat geschrieben:Die Idee hinter den Exceptions basiert auf dem Datentyp der Exception. Damit das ganze nämlich sauber und unterscheidbar bleibt, braucht jede Möglichkeit, eine Datei nicht zu finden, ihr eigene FileNotFoundException. Weicht man das ganze wieder auf, in dem man FileNotFoundException( "Preferences" ) oder FileNotFoundException( "DataFile" ) wirft, ist der Catch-Block für FileNotFoundException ein Alibi, aber keine Fehlerbehandlung, wenn eine FileNotFoundException( "swapfile" ) vorbeifliegt.
Na und? Wo liegt das Problem? Man kann im Exception- Typ ein enum definieren (siehe oben), und diesen dann im catch block abfrageg. Falls der nicht passt, wie schon gesagt: "throw away". Ist bei einem Rückgabewert auch nicht anders. Statt throw gibts halt return.
Xin hat geschrieben:Die Verantwortung schwindet, die meisten benutzen eine Exception, die sie irgendwo finden, frickeln sie was um, bauen einen merkwürdigen Konstruktor dazu und dann wird eine NameDerFirmaException geworfen, es landet alles in einem catch-Block, der anschließend total überfordert ist und kein Entwickler schafft es, den Müll wieder auseinanderzufrickeln.

Nix für ungut, aber das sind die Realitäten, wenn es eben nicht nur ein Übungsprogramm ist.
Wenn das so ist, dann ist das einfach nur ein Programmierfehler. Wenn sie irgend eine Exception finden und sie "umfrickeln", so dass es für sie passt ist das dann das Problem des Entwicklers. Ein Sprachfeature kann nicht den Programmierer ersetzen, der es auch korrekt verwenden muss. Allerdings muss man bei Fehlerbehandlung (in welcher form auch immer) konsequent bleiben, sonst hat man eben die "Frickelei".
Das mit dem Catch block wo dann alles landet kannst du dir Äquivalent zu einer Fehlervariable die "FILE_OPEN_ERROR" vorstellen. Wenn der programmierer es nicht für nötig hält, den Fehler genauer zu spezifizieren (FILE_NOT_FOUND, PERMISSION_DENIED...), musst du den Fehler auch "herausfrickeln".
Xin hat geschrieben:Die Fehlerbehandlung verliert sich in allen Quelltexten. Ein Rückgabewert reicht bis zur nächsten Ebene und da findet die Fehlerbehandlung statt - oder nicht, aber dann weiß man zumindest, wo sie nicht stattfindet.
Genau so ist es mit exceptions auch (oder es sollte so sein). Eine Funktionsdeklaration kann einen throw() specifier haben, leider ist der mehr zu Dokumentationszwecken da als sonst zu etwas. Der Programmierer muss die Exception abfangen, (äquiv. zu rückgabewert überprüfen) behandeln und dann weiterwerfen, wenn es notwendig ist (äquivalent zu return E_SOMETHIN;).
Xin hat geschrieben:Und wie Du selbst sagst: Wenn die Exception durchkracht ist es ein Bug. Wenn sie sich verheddert ist es auch ein Bug, aber man sieht ihn nicht. Weißt Du wie oft Exceptions abgefangen werden, obwohl man dieses Verhalten nicht wünschte? Weil irgendeine Oberklasse von einem Programmierer eingefangen irgendwo wird, dem gar nicht bewußt ist, was da alles vorbeifliegen kann? Der behandelt den Fehler, das Programm bleibt in einem undefinierten Zustand und dann gucken wir mal, wie lange es noch läuft.
Wenn das so ist, dann sollten sich die Programmierer mal überlegen nicht von "irgendwelchen oberklassen" abzuleiten, sondern eine Klassenhierarchie aufzubauen, die der "Fehlerhierarchie" entspricht.

Xin hat geschrieben:"Du kannst"... es macht aber keiner... und schonmal gar nicht, wenn "Du" nicht für den Bereich verantwortlich bist, der die Exception *DEINER MEINUNG* nach abfangen sollte. Der Entwickler soll eine lästige Aufgabe erfüllen und Deine Fehlerbehandlung machen? Es ist wahrscheinlich, dass der der Meinung ist, dass Du die Fehlerbehandlung selbst machen sollst, oder er weiß gar nicht so genau, was Du da eigentlich willst, jetzt kommt da eine Exception an mit der er nix anfangen will und eigentlich muss er sich um ganz andere Dinge kümmern - also was tun. throw away. Problem gelöst.willst.
Wer ist "keiner"? Ich weiß ja nicht wie du das machst, aber wenn ich mit einer Bibliothek Programmiere, oder den Code von anderen Leuten verwende, dann habe ich die referenz stehts daneben liegen, und sehe was für Exceptions die Funktion wirft.
Ich kann zum Beispiel auch nix mit einem Rückgabewert anfangen, der nicht 0 ist. Da müsste ich auch den Fehler der anderen beheben. Also was tun? return errcode;

Xin hat geschrieben:Diese Form von Auto-Pointern ist eine Krücke, wie Du sie nennst. Und es ist eine Krücke.
fat-lobyte hat geschrieben:Was ich eigentlich meine weißt du wahrscheinlich genau: Wenn ich mir Krücken baue, um ein Ziel zu erreichen, das man mit vollwertigen Sprachfeatures viel leichter und sicherer erreichen könnte, dann läuft etwas falsch.
Du benutzt AutoPointer-Krücken, weil Sprachfeatures fehlen.
Es läuft etwas falsch.
Eine Krücke für _welches_ Sprachfeature? Ein new- das den Programmierer bevormundet, wann das Objekt gelöscht werden soll? Oder müsste es etwas geben, was dem Programmierer erlaubt zu bestimmen, dass das Objekt gelöscht wird, wenn es "out of scope" geht? Hey, moment mal, das nennt sich shared_ptr, und ist ab gcc 4.0 oder Visual Studio 9.1 (oder so) im namespace std::tr1 drinnen, und ab 2009 teil der Standardbibliothek.
Damit wäre die Krücke nämlich das Sprachfeature selbst, also eigentlich gar keine Krücke mehr, sondern eine Lösung.

Xin hat geschrieben:Ähnliches der finally-Block: Da stürzt man im Blindflug rein, keine Ahnung, in welchem Zustand das Objekt grade ist, weil die Statusinformationen, die man zwangsläufig besitzt, wenn man Funktion für Funktion abprüfend abarbeited, erst mühevoll rekonstruiert werden müssen.
Hier wird Fehlerbehandlung nicht nur lästig, hier wird sie auch sehr aufwendig und vieles lässt sich einfach mit einem weiteren throw beiseite wischen zu jemanden, der nicht weiß, was er damit anfangen soll.
Man sieht, anscheinend hast du noch nie mit Exceptions in C++ gearbeitet, denn in C++ gibt es keine "finally" Blöcke. Deren Zweck wird von Destruktoren erfüllt.
Vielleicht solltest du den Exceptions mal eine Chance geben, denn anscheinend kennst du Exceptions nur von Java. Wenn das wirklich so ist, dann kann ich deine Abneigung schon fast verstehen, denn dort kriegt man von jeder Funktion aus der JavaAPI eine um die Ohren geworfen. Ich kann dir versichern: in C++ und mit der Standardbibliothek wirst du zu nichts gezwungen, du hast immer die Wahl zwischen Fehlerbehandlung mit Exceptions und klassischer Fehlerbehandlung.
Xin hat geschrieben:Nochmals: Ich bin nicht gegen Exceptions, aber ich bin absolut gegen die Verwendung von Exceptions als Regel. Dass Dateien nicht gefunden werden ist keine Ausnahme.
Ich bin dagegen, für alle Probleme vordefinierte Regeln und Lösungen zu verwenden. Wenn mir die Situation vorteilhaft erscheint, verwende ich exceptions, ansonsten nicht. Das gilt übrigens für alle Sprachfeatures: Ich finde man sollte tief in den Sack mit Möglichkeiten greifen, und die passendste rausholen, anstatt an Binsenweisheiten festzuhalten.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von Kerli » Sa Jul 26, 2008 7:39 pm

Xin hat geschrieben:
Kerli hat geschrieben:Aber durch die Verwendung von Exceptions erspare ich mir direkt im Konstruktur und in 'run' jedes 'new' zu überprüfen.
Stimmt. statt if(), try {} catch catch catch catch...
Nein es ist nur ein try{} catch nötig in dem der ganze benötigte Speicher angfordert wird.
Xin hat geschrieben:
Kerli hat geschrieben:

Code: Alles auswählen

DemoClass *demo;
try
{
  demo = new DemoClass();
  demo->run();
  printf( "Success\n" );
}
catch(std::bad_alloc&)
{
  printf( "No Memory\n" );
}
catch(...)
{
  printf( "Failed\n" );
}
delete demo;
Und was machst du eigentlich wenn 'run' fehlschlägt?
Eben... die Frage stelle ich Dir ja auch andauernd und trotzdem baust Du hier noch eine Lösung zusammen, die genau diese Frage auch nicht beantwortet. Schade eigentlich.
Warum, die Lösung ist doch schon in dem Beispiel. Sollte in 'run()' eine Speicheranforderung fehlschlagen, dann fang ich die Exception außerhalb auf, und auch wenn sonst etwas schiefgeht, dann kann ich auch alle anderen Exceptions auffangen und gib dann genau die gleiche Fehlermeldung wie du mit deinem Code aus.
Xin hat geschrieben:
Kerli hat geschrieben:Wie kannst du dann festellen was fehlgeschlagen ist? Du kannst zwar zum Beispiel einen Integer als Rückgabetyp wählen und dann darüber verschieden Fehlercodes bekommen, aber sehr flexibel ist das nicht. Was ist wenn du einen neuen Fehlertyp hinzufügst? Dann müsstest du wieder einen neuen Fehlercode definieren, währenddessen ich mit Exceptions einfach einen Beschreibenden String übergeben kann und den ohne irgendeine Interpretation eines Fehlercodes direkt ausgeben oder verwenden kann.
Sekunde... Du wirst Exceptions (AUSNAHMEN), um Strings weiterzuleiten... dafür brauche ich nur ein char * zurückzugeben.
Sorry, noch verfehlter kann eine Exception kaum verwendet werden.
Nein, ich will keine Strings weiterleiten, sondern statt einem Fehlercode einen Fehlerstring weitergeben. Oder findest du etwa die alte Variante mit verschiedensten Funktionen a la 'getLastError()' oder 'getErrorString(int error_code)' schön ist? Und wenn man dann noch verschiedene Bibliotheken hat, dann muss man noch für jede Bibliothek die richtige Funktion aufrufen.
Xin hat geschrieben:
Kerli hat geschrieben:Das ist wieder die Verantwortung des Programmierers was er damit macht. Wenn man zum Beispiel keinen Speicher mehr bekommt, dann kann man ja trotzdem noch die wichtigen Daten speichern und erst dann das Programm beenden.
Könnte er, wenn er wüßte, was schief gegangen ist. Wie willst Du denn Daten speichern, Du weißt ja nichtmals ob Du überhaupt ein Objekt hast.
Ich würde ja auch nur bereits vorhandene Objekte speichern. Wozu sollte ich denn gerade neu erzeugte Objekte speichern, wenn man daran sowieso noch keine Änderungen vorgenommen hat.
Xin hat geschrieben:Fehlerbehandlung ist lästige Arbeit. Arbeit wird gerne weitergereicht und throw macht genau das.
Und gerade ohne Exceptions wird Fehlerbehandlung oft vernachlässigt, weil man einen Rückgabewert nicht überprüfen muss. Eine Exception hingegen muss man auffangen.
Xin hat geschrieben:Nochmals: Ich bin nicht gegen Exceptions, aber ich bin absolut gegen die Verwendung von Exceptions als Regel. Dass Dateien nicht gefunden werden ist keine Ausnahme.
Hat das einer von uns jemals so gesagt? Ich überprüfe auch ohne Exceptions ob eine Datei geöffnet werden konnte...
Aber es gibt durchaus auch sinnvolle Anwendungsfälle. zb:

Code: Alles auswählen

SomeClass *p_class;
int *p_int;
Data *p_data;
/*...*/
try
{
  p_class = new SomeClass();
  p_int = new int();
  p_data = new p_data( p_class, p_int );
  /*...*/
}
catch(std::bad_alloc&)
{
  std::cout << "Out of mem" << std::endl;
}
Wie würdest du so etwas ohne Exceptions vernünftig machen? Du müsstest ja nach jedem new händisch überprüfen ob du den gewünschten Speicher auch bekommen hast.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von Xin » So Jul 27, 2008 1:43 am

fat-lobyte hat geschrieben:Du kannst übrigens auch Exceptions von typ bool werfen, wenns dir Spaß macht - nur so nebenbei. sqrt() ist wirklich ein ziemlich schlechtes Beispiel, da in solchen Fällen (zeitkritische Funktionen) ein assert() nicht schaden kann.
Hier wird es witzlos, natürlich kann ich bool werfen, ich kann auch einfach eine Funktion doSomethingUseless() aufrufen und einfach return false; zurückgeben.

sqrt() habe ich mir nicht ausgesucht, sie wurde hier angeführt, um die Vorteile von Exceptions aufzuzeigen. sqrt() ist immer ein schlechtes Beispiel, wenn sich herausstellt, dass es nunmal nicht taugt, um Exceptions zu propagieren.
Ich kann gute Beispiele für Exceptions konstruieren, aber ich habe noch nie jemanden gesehen, der ein gutes Beispiel konstruierte und gleichzeitig für Exceptions war. Wer gute Beispiele konstruiert, weiß, dass sie Exceptions dafür nicht verwendet werden.
Ein Programm, dass über mehr als 5 Ausnahmen verfügt missbraucht Exceptions. Und eigentlich denke ich, dass 2 schon über die obere Grenze hinausgeht.
fat-lobyte hat geschrieben:
Xin hat geschrieben:Der Aufwand ein E_SOMETHING zu deklarieren ist wesentlich geringer als für alles eine eigene Exception zu erzeugen.
Der aufwand in jeder Funktion die Fehlschlagen kann einen Zeiger oder Referenz als Argument zu übergeben, weil der Rückgabewert nicht verwendet werden kann ist wesentlich höher als für eine Art von Fehler einen Typen zu deklarieren und ihn dann mehrmals zu verwenden.
Hier kommen wir in den Bereich der Meinungen rein. Ich sehe daraus, dass Du eben nicht für jeden Fehler einen Typ konstruierst, sondern Typen viel häufiger wiederverwendest, als Du tun dürftest, um die Sache sauber zu machen.
fat-lobyte hat geschrieben:
Xin hat geschrieben:Die Leute sind wesentlich eher bereit, eine Zeile mit einem 'const int' zu definieren als eine neue Klasse.
Wer sind "Die Leute"? Sprichst du für dich, hast du recht. Ich zum Beispiel habe kein Problem eine Klasse zu definieren, um sie dann als Exception zu verwenden.

Code: Alles auswählen

struct FileError 
{
    enum {FILENOTFOUND = 0} reason;
    std::string msg;
}
Das war nicht allzu lang oder?
Ich habe Wertetabellen, die sind so groß, dass ich 'static unsigned int const' als SUIC definiere, weil es mir zu lang ist 'static unsigned int const' zu schreiben.
Jetzt mach mal 2000 dieser Strukturen davon. Viel Spaß.
'Die Leute' sind die Programmierer, die mir diesbezüglich aufgefallen sind, das ist natürlich keine Verallgemeinerung auf alle, eventuell habe ich auch nur die gesehen, die dazu keinen Bock hatten oder einfach dafür kein Geld bekamen.

Dein Enum zeigt übrigens, dass Du wieder beliebig viele "FileErrors" in einer catch-Anweisung erschlägst. Du hast also in Deinem Catch-Block die Frage zu beantworten, was eigentlich passiert ist und wer den Fehler geworfen hat. Aus einem einfachen Programmfluss entwickelt sich in der Fehlerbehandlung ein zweidimensionales Problem in einem catch Block. Geilomat.
fat-lobyte hat geschrieben:
Xin hat geschrieben:Nix für ungut, aber das sind die Realitäten, wenn es eben nicht nur ein Übungsprogramm ist.
Wenn das so ist, dann ist das einfach nur ein Programmierfehler. Wenn sie irgend eine Exception finden und sie "umfrickeln", so dass es für sie passt ist das dann das Problem des Entwicklers. Ein Sprachfeature kann nicht den Programmierer ersetzen, der es auch korrekt verwenden muss. Allerdings muss man bei Fehlerbehandlung (in welcher form auch immer) konsequent bleiben, sonst hat man eben die "Frickelei".
Exceptions fördern diese Frickelei.
Wenn alle Entwickler vorbildlich programmieren würden, müsste keine Software debuggt werden. Ein Großteil von Algorithmen ist Fehlerbehandlung => ein Großteil der Programmierfehler findet sich in der Fehlerbehandlung.
fat-lobyte hat geschrieben:Das mit dem Catch block wo dann alles landet kannst du dir Äquivalent zu einer Fehlervariable die "FILE_OPEN_ERROR" vorstellen. Wenn der programmierer es nicht für nötig hält, den Fehler genauer zu spezifizieren (FILE_NOT_FOUND, PERMISSION_DENIED...), musst du den Fehler auch "herausfrickeln".
Wenn ich sauber arbeite, muss ich keinen Fehler generieren, denn ich weiß an Ort und Stelle, was schief gegangen ist.
fat-lobyte hat geschrieben:Wenn das so ist, dann sollten sich die Programmierer mal überlegen nicht von "irgendwelchen oberklassen" abzuleiten, sondern eine Klassenhierarchie aufzubauen, die der "Fehlerhierarchie" entspricht.
Der Traum vom Software vom Reißbrett.
Der wird in der Softwaretechnik häufiger geträumt.

Eine Realität in die ich nun eintreten darf ist folgende: 20 Jahre Sourcecodes, verschiedene Sprachen, verschiedenene Entwickler und entsprechend unterschiedliche Fähigkeiten. Die meisten sind keine Informatiker.
Die Realität, die ich kürzlich noch hatte: 7 Jahre Sourcecode, verschiedene Sprachen, sehr viel mit Java-Exceptions, nichts passt zusammen. Die Entwickler wissen es, aber tun kann man daran nix mehr. Weder ist der Fehlerverlauf brauchbar nachvollziehbar, noch behandelbar. Eine Fehlerbehandlung in dem Sinne findet selten statt, so dass auf Fehler korrigierend eingewirkt wird => es läuft oder es knallt.

Solange Progammierung ein Hobby ist, kann man propagieren, was das Ideal ist und darf vom idealen Entwickler ausgehen. Als professioneller Entwickler ist es mein Job zu berücksichtigen, dass Entwicklung nicht professionel ist. Entwicklung ist nicht ausleben der Kunst, ein perfektes Programm zu schreiben, sondern ein Problem bezahlbar zu lösen. Und damit dafür brauche ich absolut alles, was mir eine Sprache anbieten kann, um den Zustand der Software zu kontrollieren.
Exceptions bieten mir eine Möglichkeit, Informationen über den Zustand der Software zu vernichten.
fat-lobyte hat geschrieben:Wer ist "keiner"? Ich weiß ja nicht wie du das machst, aber wenn ich mit einer Bibliothek Programmiere, oder den Code von anderen Leuten verwende, dann habe ich die referenz stehts daneben liegen, und sehe was für Exceptions die Funktion wirft.
Hahaha... Du hast eine Referenz...

In der Regel hast Du einen Sourcecode und der macht irgendwas und Dein Job ist, dass er jetzt noch irgendwas anderes macht. Irgendwie. Möglichst in 2 Stunden.

fat-lobyte hat geschrieben:Eine Krücke für _welches_ Sprachfeature? Ein new- das den Programmierer bevormundet, wann das Objekt gelöscht werden soll? Oder müsste es etwas geben, was dem Programmierer erlaubt zu bestimmen, dass das Objekt gelöscht wird, wenn es "out of scope" geht? Hey, moment mal, das nennt sich shared_ptr, und ist ab gcc 4.0 oder Visual Studio 9.1 (oder so) im namespace std::tr1 drinnen, und ab 2009 teil der Standardbibliothek.
Damit wäre die Krücke nämlich das Sprachfeature selbst, also eigentlich gar keine Krücke mehr, sondern eine Lösung.
AutoPointer sind schon gut und richtig, aber indem man die Libs erweitert, stärkt man die Sprache nicht.
fat-lobyte hat geschrieben:Man sieht, anscheinend hast du noch nie mit Exceptions in C++ gearbeitet, denn in C++ gibt es keine "finally" Blöcke. Deren Zweck wird von Destruktoren erfüllt.
Vielleicht solltest du den Exceptions mal eine Chance geben, denn anscheinend kennst du Exceptions nur von Java. Wenn das wirklich so ist, dann kann ich deine Abneigung schon fast verstehen, denn dort kriegt man von jeder Funktion aus der JavaAPI eine um die Ohren geworfen. Ich kann dir versichern: in C++ und mit der Standardbibliothek wirst du zu nichts gezwungen, du hast immer die Wahl zwischen Fehlerbehandlung mit Exceptions und klassischer Fehlerbehandlung.
Ich habe schon länger nicht mehr mit Exceptions gearbeitet, warum sollte ich mit einem Feature arbeiten, dass ich für eine Fehlentwicklung halte. Ein Algorithmus ist nur dann schön, wenn er keine Ausnahmen kennt, das ist meine Maxime.
Ich schreibe einen Compiler, hier gibt es bisher keine Ausnahmen, ausschließlich die Regel. Es ist nicht unwahrscheinlich, dass es eine Exception geben wird, die den Compiler vollständig abbricht, wenn der Aufbau des Compilerbaums nicht möglich ist.
Eine Ausnahme.


Zwischen finally und Destruktor sehe ich massive Unterschiede. Nehmen wir an, Du musst ein FILE * schließen, wie machst Du das?
Einen Stream nehmen? Sorry, die Lib, die Du verwendest frisst nur FILE*. AutoPtr? Der ruft delete, nicht aber fclose(). Neues Objekt erstellen, dass im Falle eines Fehlers im Destruktor fclose() ruft.
Das ganze nochmal für CloseLibrary(). Und free(), weil die Lib Dir nunmal einen Pointer auf einen mit malloc()-generierten Datensatz liefert. Und unlock(), weil sonst die Library später nicht mehr verwendet werden könnte.
Jede Wette: 90% derartiger Probleme sind falsch gelöst.
Zum Glück ist der Fehlerfall nicht so häufig, dass während des Programmablaufs die File-Handles ausgehen. Und wenn sowas wie unlock() schief geht... gibt's jemanden, der sich darüber wundert, wenn er Windows neustarten muss, damit irgendwas läuft?
fat-lobyte hat geschrieben:
Xin hat geschrieben:Nochmals: Ich bin nicht gegen Exceptions, aber ich bin absolut gegen die Verwendung von Exceptions als Regel. Dass Dateien nicht gefunden werden ist keine Ausnahme.
Ich bin dagegen, für alle Probleme vordefinierte Regeln und Lösungen zu verwenden. Wenn mir die Situation vorteilhaft erscheint, verwende ich exceptions, ansonsten nicht. Das gilt übrigens für alle Sprachfeatures: Ich finde man sollte tief in den Sack mit Möglichkeiten greifen, und die passendste rausholen, anstatt an Binsenweisheiten festzuhalten.
Das sehe ich ähnlich, nur greife ich in Schlussfolgerung dazu sehr selten zu Exceptions. In C++ derzeit in keinem aktiven Projekt.

----------------

Kerli hat geschrieben:Nein es ist nur ein try{} catch nötig in dem der ganze benötigte Speicher angfordert wird.
Keine Ahnung, was Du für Algorithmen schreibst, aber meine Algorithmen brauchen am Anfang speicher, wissen aber noch nicht, ob sie in der Mitte nochmal Speicher brauchen. Ich kann am Anfang keine Liste aufführen, und alles in einen Try-Block packen, ich brauche dafür mehrere oder ich fordere reichlich Speicher an, von dem ich die Hälfte nie brauche.

Beispiele konstruieren ist schön, aber sie sollten schon überzeugen.

Kerli hat geschrieben:
Xin hat geschrieben:
Kerli hat geschrieben:Und was machst du eigentlich wenn 'run' fehlschlägt?
Eben... die Frage stelle ich Dir ja auch andauernd und trotzdem baust Du hier noch eine Lösung zusammen, die genau diese Frage auch nicht beantwortet. Schade eigentlich.
Warum, die Lösung ist doch schon in dem Beispiel. Sollte in 'run()' eine Speicheranforderung fehlschlagen, dann fang ich die Exception außerhalb auf, und auch wenn sonst etwas schiefgeht, dann kann ich auch alle anderen Exceptions auffangen und gib dann genau die gleiche Fehlermeldung wie du mit deinem Code aus.
Sie gibt nicht die gleiche Fehlermeldung aus, sie gibt 'No Memory' statt 'Failure' aus.
Dass run() nicht durchläuft ist nicht gleichbedeutend mit einem nicht existierendem Objekt. Das Objekt kann in der Lage sein, andere Aufgaben zu lösen. Ein zweiter Anlauf mit run(), zum Beispiel mit dem 2. Datensatz, kann erfolgreich laufen.
Du hast keine Information, ob run() ein Problem hat, oder ob das Objekt gar nicht existiert.

Bitte verkauf mir eine halbgare Lösung nicht als Vorteil.
Tut mir leid, wenn ich das so sage, aber Du dokterst hier schon seit einiger Zeit mit Exceptions rum, obwohl Dir noch ein Problembewußtsein fehlt, was es eigentlich bedeutet, wenn ein Fehler auftritt.
Deine Exceptionlösungen sind nicht equivalent, teilweise sind sie sogar falsch. Das hier ist nur ein Thread, eine Diskussion, hier werden keine Codes getestet, aber es nutzt nichts, zu debatieren, wenn wir nicht auf gleichem Level Fehlerbehandlung durchführen.
Heißt: Entweder Du bohrst Dein Beispiel entsprechend auf, oder Du packst noch die Fehlerbehandlung dazu, die bad_alloc in ... umwandelt, damit das ganze funktioniert.
Kerli hat geschrieben:Nein, ich will keine Strings weiterleiten, sondern statt einem Fehlercode einen Fehlerstring weitergeben. Oder findest du etwa die alte Variante mit verschiedensten Funktionen a la 'getLastError()' oder 'getErrorString(int error_code)' schön ist? Und wenn man dann noch verschiedene Bibliotheken hat, dann muss man noch für jede Bibliothek die richtige Funktion aufrufen.
GetLastError() hat den Nachteil (üblicherweise) keine History mitzuführen, dafür verbraucht GetLastError() keine Rechenzeit, um Informationen mitzuschleifen, die häufig nicht interessieren.
Schön ist die falsche Priorität hier, es gibt keine schöne Lösung, es gibt nur Lösungen die sinnvoll oder weniger sinnvoll sind.
Kerli hat geschrieben:Aber es gibt durchaus auch sinnvolle Anwendungsfälle. zb: ...
Kerli hat geschrieben:Wie würdest du so etwas ohne Exceptions vernünftig machen? Du müsstest ja nach jedem new händisch überprüfen ob du den gewünschten Speicher auch bekommen hast.
Vollkommen richtig und ich würde in jedem Fall wissen, wie der Status meines Algorithmus' ist und die Bedeutung abschätzen können.
Speicheranfordern kann innerhalb eines Algorithmus' häufiger vorkommen. Dass die Sachen hintereinander vorkommen ist ungewöhnlich, was das Beispiel zwar nett, aber vergleichsweise uninteressant macht.


Im Prinzip würde ich die Diskussion hier gerne einstampfen, denn ich führe sie nicht zum ersten Mal und die Texte werden auch nicht kürzer.
Und sie nimmt den Verlauf, den sie meistens nimmt, grade mit jüngeren Entwicklern, die glauben, dass die Mittel, die sie an der Hand haben gut sind und dass Entwickler den Verlauf einer Entwicklung kontrollieren können. Die einzige relativ zuverlässige Kontrolle, die existiert, ist die semantische Prüfung durch den Compiler und jedes Feature, dass die Kontrolle des Compilers zu leicht aushebelt, arbeitet gegen mich.
Als zweite Instanz folgen Unit-Tests, die sind nicht zuverlässig, weil unvollständig, aber sie liefern zumindest Indizien.
Der Mensch ist - auch als professioneller Entwickler - ist nur Richtungsgeber, aber nicht in der Lage, korrekte Fehlerbehandlung zu garantieren. Mehr als versuchen kann er es nicht. Ein fortgeschrittener Entwickler kann hervorragend programmeiren können, wunderbar debuggen und kennt alle Tricks, Fehler zu vermeiden. Ein professioneller Entwickler sollte sich aber genauso intensiv um die Fehler kümmern, die er nicht greifen kann und das bedeutet in erster Linie dafür zu sorgen, dass der Mensch möglichst nicht in die Lage versetzt wird, zuverlässige Kontrollen auszuhebeln und dabei doch alle Möglichkeiten behält, Anweisungen zu geben. Ein fortgeschrittener Programmierer muss nur eine Stunde Kopfschmerzen haben und passt an der entscheidenen Stelle nicht auf und dann bleiben nur noch semantische Analyse und Unit-Tests, um das abzufangen. Und Unit-Tests gibt es meistens nicht. Man wird für Features bezahlt, nicht für Unit-Tests.
Darum setzte ich die Casts (im anderen Thread) in Funktionen, weil hier der Kontrollverlust wenigstens einmalig an klar definierten Stellen stattfindet. Darum bin ich gegen Exceptions, weil sie dazu verleiten, Typen wiederzuverwerten und damit die semantische Analyse untergraben und so absichtlich, wie versehentlich, Probleme zusammenfassen, die wie Kerli mit seinem Beispiel zeigt, nicht zusammengehören.

Das zusätzliche if ist günstiger als Exceptions. Man kann es weglassen, aber ein fortgeschrittener Entwickler, der so arbeitet, sieht, wenn Resourcen angefordert werden ohne dass ein Wächter da steht, oder der Algrithmus in die Breite geht. Für einen erfahrenen Entwickler steht genau an der Stelle ein 'FALSCH' im Code, während bei Exceptions die Fehlerbehandlung irgendwo anders passiert - oder auch nicht, das sieht man eben nicht.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: (Un-)Sinn von Exceptions

Beitrag von Kerli » So Jul 27, 2008 10:47 am

Xin hat geschrieben:
Kerli hat geschrieben:Aber es gibt durchaus auch sinnvolle Anwendungsfälle. zb: ...
Kerli hat geschrieben:Wie würdest du so etwas ohne Exceptions vernünftig machen? Du müsstest ja nach jedem new händisch überprüfen ob du den gewünschten Speicher auch bekommen hast.
Vollkommen richtig und ich würde in jedem Fall wissen, wie der Status meines Algorithmus' ist und die Bedeutung abschätzen können.
Speicheranfordern kann innerhalb eines Algorithmus' häufiger vorkommen. Dass die Sachen hintereinander vorkommen ist ungewöhnlich, was das Beispiel zwar nett, aber vergleichsweise uninteressant macht.


Im Prinzip würde ich die Diskussion hier gerne einstampfen, denn ich führe sie nicht zum ersten Mal und die Texte werden auch nicht kürzer.
Dann lassen wir das am Besten mit der Diskussion, weil ich glaub das würde sonst nie zu einem Ende kommen, bei diesen so verschiedenen Standpunkten ;)

Ich möchte aber nur noch anmerken, dass gerade bei meinem letzten Projekt ein Code vorgekommen ist wo ich wie in meinem Beispiel ein paar Speicheranforderungen direkt hintereinander gehabt habe, und da war es mit Exceptions sicher die schönere Lösung.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Antworten