Was genau bringen exceptions?



  • Artchi schrieb:

    Aber jeder kann aus der dunkelsten Ecke hier als unbekannter Feigling Porno-Links posten.

    Porno-Links?? Wo denn?
    Sind Registrierte denn bessere Porno-Links-Poster? Hier klicken!

    Jeder kann mich beleidigen.

    Du verwechselst da was. Du warst der, der beleidigt hat. Von deinem elitären Gehabe ganz zu schweigen.

    Die Frage ist: würde er das machen, wenn er vor mir stehen würde??? (auch wenn ich ihn nicht kenne!) Nein! Würde er ebend nicht.

    Man kann viel behaupten, wenn der Tag lang ist. Du hast doch nicht die geringste Ahnung, wie derjenige sich Angesicht zu Angesicht verhalten würde.



  • @all! Man sollte wieder ontopic werden.

    Es ist jedenfalls nicht richtig, das Exceptions in C++ nicht richtig unterstützt werden, nur weil man nicht gezwungen wird, die explizit mit einem try-catch abzufangen. Das ganze hat auch Designgründe, die jeder in den Mailings des C++-Komitees nachlesen kann. Auch Herb Sutter hat sich schon mal in seinem Blog dazu geäussert. Excpetions in C++ arbeiten und funktionieren. Designentscheidungen als unfunktionstüchtig abzuwerten und zu sagen, in Sprache X sind sie deshalb besser, ist einfach unbegründet.



  • Hi,

    ich wollte auch mal was zum Thema sagen (und nicht zum Flame).

    Simon2 hat ein sehr gutes Beispiel gemacht, wo Exceptions den Code vereinfachen. Mann kann dem Algorithmus besser folgen, wenn er nicht durch permanente Fehlerabfragen unterbrochen wird.

    Beispiel ohne Exceptions:

    int f()
    {
      int ret;
      ret = f1();
      if (ret != 0)
        return ret;
      ret = f2();
      if (ret != 0)
        return ret;
      ret = f3();
      if (ret != 0)
        return ret;
    }
    

    Das gleiche mit Exceptions:

    void f()
    {
      f1();
      f2();
      f3();
    }
    

    Ich nehme einfach mal an, die Funktion f führte einen Algorithmus, bestehend aus 3 Unterfunktionen. Im Fehlerfall einer Unterfunktion soll der Fehler einfach nur weiter gereicht werden. Ich denke, die Funktion mit Exceptions ist wesentlich übersichtlicher.

    Und noch ein Kommentar zu Java vs. C++:

    Java verlangt, daß eine Methode an der Schnittstelle die Exceptions, die es auslöst auch bekannt gibt. Das klingt gut und hat seine Vorteile. Der Benutzer der Schnittstelle weiß, welche Fehlerzustände geliefert werden können, wenn er nur die Schnittstelle sieht.

    Die Kehrseite ist, daß ich beispielsweise in abgeleiteten Klassen, die Methoden überschreiben, keine neuen Fehlerzustände einführen kann. Man stelle sich eine Klasse mit einr Getter-Methode vor. Diese wirft keine Exception. In einer abgeleiteten Klasse überschreibe ich die Methode, um den Wert beispielsweise aus einer Datenbank zu lesen. Was mache ich dann bei Datenbankfehlern? Bei Java habe ich dann ein Problem, da mir die Methodensignatur der Basisklasse verbietet, Fehler zu melden.

    Ich hoffe, das war neutral genug formuliert, so daß ich keinen Brennstoff für weitere Flames gegeben habe. Welche Technologie ich bevorzuge, sieht man spätestens in meiner Signatur.

    Tntnet



  • Irgendwie erinnert mich der Ausdruck "Lern lesen!" an etwas...http://www.zfx.info/DisplayThread.php?TID=3723 🤡
    @Artchi: Bin nicht der Meinung, dass Registrierte automatisch zu Persönlichkeiten werden, man nehme mich als Beispiel. Du weisst nichts von mir, hab ich deswegen mehr Respekt verdient als ein Unreg? Nur mal als Denkanstoss.

    @Topic:
    Nur weil man nicht gezwungen wird, Exceptions zu fangen, sind sie nicht schlecht. Es zwingt dich auch niemand Klassen zu verwenden, oder Templates. Aber sie sind verfügbar und ein gewissenhafter Programierer wird sie einsetzen, wo sinnvoll und nötig. In Java wird man gezwungen zu gewissen Sachen, das nimmt einem aber ab zu denken. Ob das gut ist lass ich mal offen.



  • Hm, ich sollte es sein lassen aber hab grad nichts zu tun.

    @CStoll: Ich will dich nicht nerven oder so, aber hat der "Ford Perfect" mit seinem Java-Einwurf nicht die Sache ins Rollen gebracht. Deswegen hab ich die alte gute Java und C++ Liebe herangezogen, da dort immer ein "wenig" übertrieben wird. Sei es beim peinlichsten finden von Fehlern (sogar Rechtschreibfehler werden dann wichtig um Thesen zu wiederlegen 🙄 ) oder darlegen von Features mit praxisfremden Beispielen.

    @anderer anonymer: Ich weiss nicht was du hast, die Beiträge sind fast immer fachlich korrekt. Und falls du länger in dem Forum stöberst findest du richtige Diamantensplitter an kurzer und prägnanter Form, welche ich in so machen Forum misse.
    Es ist natürlich dir überlassen was du wie findest, jedoch musst du auch bedenken das alle die hier regelmässig Posten vorbelastet sind. Da die meisten Flames die du hier lesen kannst von unregestrieten ins Rollen gebracht wurden. Das Ding ist ja leider, es macht machen es regelrecht spass die Fehler von anderen möglichst provozierend zu berichtigen. Das ohne Mimik ist der Tot für jeden Thread mit einem Thema ausser Flame 😉

    In dem Sinne, eine Stimme für ausseinanderbröckeln.
    Wählende Baracke

    PS: Werd nimma in den Thread posten, versprochen (Wehe ihr fang jetzt an über mich herzuziehen ^^).



  • Baracke schrieb:

    @CStoll: Ich will dich nicht nerven oder so, aber hat der "Ford Perfect" mit seinem Java-Einwurf nicht die Sache ins Rollen gebracht. Deswegen hab ich die alte gute Java und C++ Liebe herangezogen, da dort immer ein "wenig" übertrieben wird. Sei es beim peinlichsten finden von Fehlern (sogar Rechtschreibfehler werden dann wichtig um Thesen zu wiederlegen 🙄 ) oder darlegen von Features mit praxisfremden Beispielen.

    Möglich, daß "Java" der Auslöser ist, aber der ganze Flame-War hat inzwischen herzlich wenig damit zu tun (und daran sind leider nicht nur die Unregs schuld).

    anderer anonymer schrieb:

    hallo,
    ich bin zwar weder "fordperfect" noch "anonymer assi", aber recht haben sie trotzdem. ihr (artchi, simon) spielt euch hier echt auf, das ist unglaublich.
    jedesmal wenn ich in diesem forum rumstöbere muss ich sowas wie in diesem thread lesen, von euch und von anderen. da vergeht einem wirklich die lust sich in so einem forum anzumelden wenn sich selbst user mit mehreren tausend beiträgen so aufführen. da bleib ich lieber auch ein "anonymer assi" hier. das ist aber kein grund mich verarschen zu lassen.
    und dat geilste is auch noch dass auf der startseite mit 900.000 von solchen postings das forum beworben wird 👍

    Von diesen 900.000 Beiträgen ist vielleicht ein verschwindend geringer Anteil, wo sich jemand "aufspielt" - und meistens sind das Antworten auf (sorry) RTFM-Fragen. Der Anteil an Troll-Postings von Unregistrierten ist da um einiges höher.



  • tntnet schrieb:

    Simon2 hat ein sehr gutes Beispiel gemacht, wo Exceptions den Code vereinfachen. Mann kann dem Algorithmus besser folgen, wenn er nicht durch permanente Fehlerabfragen unterbrochen wird.

    Beispiel ohne Exceptions:

    int f()
    {
      int ret;
      ret = f1();
      if (ret != 0)
        return ret;
      ret = f2();
      if (ret != 0)
        return ret;
      ret = f3();
      if (ret != 0)
        return ret;
    }
    

    Hinzu kommt ja, das f1, f2 und f3 zwar einen Fehlercode 1 liefern können, aber diese total unterschiedliche Bedeutung haben können. Also wenn f eine 1 zurück liefert, weiß der f-Aufrufer absolut nicht, was nun passiert ist.

    Das gleiche mit Exceptions:

    void f()
    {
      f1();
      f2();
      f3();
    }
    

    Mit Exceptions habe ich typisierte Fehler. Und ich bekomme als f-Aufrufer diesen einen bestimmten Typ zurück, und ich kann entsprechend "typisiert" reagieren. Genial!

    Java verlangt, daß eine Methode an der Schnittstelle die Exceptions, die es auslöst auch bekannt gibt. Das klingt gut und hat seine Vorteile. Der Benutzer der Schnittstelle weiß, welche Fehlerzustände geliefert werden können, wenn er nur die Schnittstelle sieht.

    Das kannst du aber auch in C++ an der Schnittstelle erkennen. Vorausgesetzt, es wurde auch entsprechend definiert. Leider macht das so gut wie niemand, was ich sehr schade finde. Ich selbst mache das in meinem Code, das ich in der Schnittstelle schreibe, was geworfen werden kann:

    void foo() throw (std::invalid_argument);
    

    LEIDER bringt MSVC hier immer eine Warnung, die ich dann mit einem pragma ausschalte. Wie es bei anderen Compilern aussieht, weiß ich leider nicht.



  • Artchi schrieb:

    ...

    Java verlangt, daß eine Methode an der Schnittstelle die Exceptions, die es auslöst auch bekannt gibt. Das klingt gut und hat seine Vorteile. Der Benutzer der Schnittstelle weiß, welche Fehlerzustände geliefert werden können, wenn er nur die Schnittstelle sieht.

    Das kannst du aber auch in C++ an der Schnittstelle erkennen. Vorausgesetzt, es wurde auch entsprechend definiert. Leider macht das so gut wie niemand, was ich sehr schade finde. ...

    Hi,

    ich bin einer von denen, die es absichtlich nicht machen (und sich damit an einen Rat von Herb Sutter halten). Für mich ist das größte Problem mit C++'s exceptions (und ein großer Vorteil bei Java), dass Verletzungen der exception specification erst zur Laufzeit ausgewertet werden. Eigentlich sind sie aber Mittel, die ich in der Designphase brauche und nicht erst beim Integrationstest.

    Ich verstehe zwar, dass man "damals" C-Kompatibilität gesucht, aber nicht, wieso man damals nicht einfach alle C-Funktionen (bzw. jede Funktion ohne excep-spec) als "throw()" definiert hat. In C kann man ja schließlich auch keine werfen und C-Wrapper für C++-Code muss man sowieso entsprechend implementieren (weil man auch in C keine fangen kann)....

    Gruß,

    Simon2.

    P.S.: Von MS habe ich nur den VC6 und der ignoriert excep-spec komplett ! (auch zur Laufzeit)



  • Schön, daß wir wieder beim Thema sind 👍

    Ich finde Exceptions auch genial. Ich habe allerdings das Gefühl, ihr habt meine 2 Ausführung nicht verstanden.

    Die Throw-Specifications finde ich persönlich unnötig und störend. Eine Basisklasse kann verhindern, daß eine abgeleitete Klasse überhaupt Fehler melden darf. Fehlerbehandlung ist doch eins der wichtigsten Dinge, wenn nicht das wichtigste überhaupt, für robuste Programme. Wenn ich eine Methode entwickele, muß ich immer die Möglichkeit haben, einfach auf Fehler zu reagieren. Sonst ist die Versuchung zu groß, darauf zu verzichten.

    Ich will meine Ausführung von vorhin nochmals an einem Beispiel erläutern.

    Man nehme eine Basisklasse:

    class MyBusinessObject
    {
        std::string attribute1;
      public:
        virtual const std::string& getAttribute1() const throw()
        { return attribute1; }
        virtual void setAttribute1(const std::string& a)
        { attribute1 = a; }
    };
    

    Hier habe ich ein Business-Objekt deklariert und mit einer default Implementierung einer Methode. Später bemerke ich, daß die Methode selten benötigt wird aber das füllen des Attributes viel Zeit in anspruch nimmt. Daher entscheide ich mich für Lazy-Init, bei dem das Attribut erst gefüllt wird, wenn es benötigt wird. Die neue Methode liest den Wert aus der Datenbank (oder holt sich das per RPC-Request oder was auch immer):

    class MyLazyBusinessObject : public MyBusinessObject
    {
      public:
        virtual const std::string& getAttribute1() const throw()
        {
           if (MyBusinessObject::getAttribute1().empty())
           {
              try
              {
                setAttribute1(
                  db.selectValue("select attribute1 from table where ..."));
              }
              catch (const myDbException& e)
              {
                 // dieser catch ist notwendig, da selectValue eine Exception auslösen könnte
                 // aber was zum Teufel mache ich jetzt mit meinem Fehler??? :rage: :warning:
              }
           }
           return MyBusinessObject::getAttribute1();
        }
    }
    

    Hier bleibt mir nichts anderes übrig, als den Fehler zu ignorieren, was tödlich ist.

    Wenn ich ein System designe, gehe ich davon aus, daß alles eine Exception werfen kann. In meinem Tntnet beispielsweise fange ich alle Exceptions auf, die die Komponente nicht selbst verarbeitet hat. Entweder ist sie von tnt::HttpError abgeleitet - dann habe ich einen HTTP-Fehlercode - oder von std::exception. Dann kommt ein HTTP-Fehler 500 - internal Server error.

    Die Einschränkung, die ich grundsätzlich mache ist, daß jede Exception direkt oder indirekt von std::exception abgeleitet sein muß. Selbst das könnte man mit catch(...) aufweichen ( 💡 sollte ich vielleicht ergänzen 💡 ).

    Für robuste Programme ist es unbedingt notwendig, daß alle Fehler beachtet werden. Und das kann ich nur garantieren, wenn es einfach ist, auf Fehler zu reagieren.

    Tntnet



  • THX 1138 schrieb:

    @Artchi: Bin nicht der Meinung, dass Registrierte automatisch zu Persönlichkeiten werden, man nehme mich als Beispiel. Du weisst nichts von mir, hab ich deswegen mehr Respekt verdient als ein Unreg? Nur mal als Denkanstoss.

    Man kann aber deine alten Beiträge lesen und sich ein Bild machen und deine zukünftigen Beiträge mit einer bestimmten Erwartungshaltung lesen. Sowas funktioniert nicht bei anonymen Postern, weshalb ich in dem Punkt mit Artchi übereinstimme: Ein anonymer Poster ist a priori erstmal ein unwichtiger Außenseiter. Er oder sie kann dieses Vorurteil natürlich widerlegen, muss das aber in jedem Posting erneut tun. Die meisten unregistrierten tun allerdings ihr möglichstes, um es zu bestätigen.

    Zum Thema Exceptions ist schon 100mal alles gesagt worden, dazu enthalte ich mich mal.



  • tntnet schrieb:

    ...

    class MyBusinessObject
    {
        std::string attribute1;
      public:
        virtual const std::string& getAttribute1() const throw()
        { return attribute1; }
        virtual void setAttribute1(const std::string& a)
        { attribute1 = a; }
    };
    

    ...

    class MyLazyBusinessObject : public MyBusinessObject
    {
      public:
        virtual const std::string& getAttribute1() const throw()
        {
           if (MyBusinessObject::getAttribute1().empty())
           {
              try
              {
                setAttribute1(
                  db.selectValue("select attribute1 from table where ..."));
              }
              catch (const myDbException& e)
              {
                 // dieser catch ist notwendig, da selectValue eine Exception auslösen könnte
                 // aber was zum Teufel mache ich jetzt mit meinem Fehler??? :rage: :warning:
              }
           }
           return MyBusinessObject::getAttribute1();
        }
    }
    

    ...

    Schönes Beispiel ... dafür, warum konsistente exception-spec sinnvoll wären. 😉

    Ich sehe hier 2 Möglichkeiten:

    • der MyBusinessObject-Entwickler hat einen Fehler gemacht mit seinem throw(); das kann passieren - sehe ich aber nicht als Argument gegen die Technik; mit demselben Argument könnte man virtual o.a. ablehnen (könnte ebenfalls ein Basisklassenentwickler falsch verwenden) oder
    • er hat das absichtlich gemacht - z.B. weil getAttribute1() in DToren aufgerufen werden können soll

    Ich gehe mal vom 2. Fall aus und jetzt habe ich bereits eine funktionierende Anwendung geschrieben:

    class MyBusinessObject_User {
       vector<MyBusinessObject*> vec;
    ... // Eine Klasse, die polymorph diverse MyBusinessObjekte verwaltet
       public:
          ~MyBusinessObject_User() {
             for(int i=0; i<vec.size(); ++i) {
                cout << "loesche Element " << vec[i]->getAttribute1() << "\n";
                delete vec[i];
             }
          }
    };
    

    Obwohl das Werfen von exceptions in DToren "böse" ist, darf ich das machen, weil mir die Basisklasse ja zusichert, dass die verwendeten Funktionen das nicht tun.

    .... Wenn Du die exc-spec nun erweiterst, knallt es bei MIR !!!

    unschön - finde ich.
    Mit der throw()-clause hat der Basisklassenentwickler festgelegt, dass das

    tntnet schrieb:

    ...Daher entscheide ich mich für Lazy-Init...

    eben gerade nicht geht (wie gesagt: Vorausgesetzt, er hat keinen Fehler gemacht).
    Ein Mißverständnis von excep-spec ist: "Ich werfe in meiner derzeitigen Implementierung nur die exception A, da gebe ich mal throw(A) an und damit ist gut !"
    Soooo darf man genausowenig zu einer excep-spec kommen, wie man andere Schnittstelleneigenschaften (access spec, Parameter, Basisklassen, ...) nur auf Basis der aktuellen Implementierung festlegen sollte. Stattdessen definiert man über seine Schnittstelle (eben auch die excep-spec), wie eine Klasse nach außen hin arbeitet bzw. womit der Aufrufer rechnen muss/worauf er sich verlassen kann.

    Und wenn mir zugesichert wird, dass

    • Alle MyBusinessObjecte bei getAttribute1() keine exception werfen (= Keine Ausnahme auftreten kann; das sichert mir der MyBusinessObjecte-Entwickler zu) und
    • MyLazyBusinessObject ein MyBusinessObject ist (was Du mir mit der Ableitung zusicherst),

    .... dann ist das nur konsistent, wenn MyLazyBusinessObject ebenfalls getAttribute1() keine exception wirft.

    Gruß,

    Simon2.



  • Exceptions sind wirklich ein bisschen mager. So etwas wie Conditions aus Lisp würde ich mir wünschen.



  • Ok, das reicht hier jetzt soweit. Diskussionen über (fehlende) Netiquette bitte im NADRW-Forum weiterführen. Falls es zum eigentlichen Thema Exceptions noch Diskussionsbedarf gibt, bitte ein neues Thema aufmachen und dort dann einfach mal beim Thema bleiben.


Anmelden zum Antworten