Schnitstellen in C++?


  • Administrator

    ~john schrieb:

    Auch wenn ich Herb Sutters Ausführungen zu C++ als sehr hochwertig betrachte, ist dies in Bezug auf Exceptions definitiv nicht der Fall. Es gibt in C++ keinerlei logischen einen Destruktor nicht als throw() zu definieren, da Exception werfende Destruktoren unweigerlich zu Problemen führen, und früher oder später eine zweite Exception geworfen wird -> terminate(). Da aber in Falle eines werfenden Destruktors das ganze nicht mehr deterministisch passiert, ist der Fehler faktisch schwer zu finden. Daher sollten Destruktoren immer throw() definiert sein.

    1. Du solltest lesen, was Herb Sutter da geschrieben hat. Ich zitiere es gerne mal:

    Moral #2: Except possibly an empty one, but if I were you I’d avoid even that.

    Boost’s experience is that a throws-nothing specification on a non-inline function is the only place where an exception specification “may have some benefit with some compilers” [emphasis mine]. That’s a rather underwhelming statement in its own right, but a useful consideration if you have to write portable code that will be used on more than one compiler platform.

    It’s actually even a bit worse than that in practice, because it turns out that popular implementations vary in how they actually handle exception specifications. At least one popular C++ compiler (Microsoft’s, up to version 7.x) parses exception specifications but does not actually enforce them, reducing the exception specifications to glorified comments. But, on the other hand, there are legal optimizations a compiler can perform outside a function, and which the Microsoft 7.x compiler does perform, that rely on the ES enforcement being done inside each function -- the idea is that if the function did try to throw something it shouldn’t the internal handler would stop the program and control would never return to the caller, so since control did return to the caller the calling code can assume nothing was thrown and do things like eliminate external try/catch blocks. So on that compiler, because the checking is not done but the legal optimization that relies on it is done, the meaning of “throw()” changes from the standard “check me on this, stop me if I inadvertently throw” to a “trust me on this, assume I’ll never throw and optimize away.” So beware: If you do choose to use even an empty throw-specification, read your compiler’s documentation and check to see what it will really do with it. You might just be surprised. Be aware, drive with care.

    Er schliesst gerade throw() nicht vollständig aus. Mahnt aber zur Vorsicht.

    2. Ein Destruktor von einem Stackobjekt oder einem Objekt mit statischer Lebensdauer, welcher eine Exception wirft, löst automatisch ein terminate aus.

    3. Ich würde aber anstatt throw() zu nehmen, doch lieber folgendes machen, falls die Unsicherheit wirklich besteht.

    try
    {
      // ...
    }
    catch(...)
    {
      std::terminate();
    }
    

    Ich konnte sowas aber bisher By-Design ausschliessen.

    Grüssli



  • Dravere schrieb:

    Yamakuzure schrieb:

    Du liest den Text nicht.

    Doch, tue ich. Ich möchte dich also bitten, wenn ich mir schon die Zeit nehme, deine Beiträge durchzulesen, dass du mir nicht unterstellst, dass ich dies nicht tue. Ich habe einfach deinen Text missverstanden. Wahrscheinlich weil er etwas komplex ist, um einen simplen Sachverhalt zu erklären.

    Entschuldige bitte. Ich bin frustriert, weil ich mich irgendwie nicht verständlich machen kann. Außerdem bekomme ich langsam Kopfschmerzen. (Was aber an der Arbeit liegt, nicht am Forum)

    Dravere schrieb:

    Dann frage ich mich immer noch, wieso du die throw -Spezifikation verwendest. Mit dem Debugger hättest du diese Informationen schliesslich auch problemlos erhalten.

    Ich benutze den Debugger bei Bedarf, und in diesem Fall habe ich ihn nicht gebraucht, da in diesem sehr speziellen Fall die terminate() -Ausgabe bereits sofort gezeigt hat, was schief war. Die Spezifikationen werden ab einem bestimmten Stand des Projekts auch wieder entfernt, und normalerweise benutze ich sie garnicht.

    Ich habe übrigens nie behauptet, dass du main->try->catch als ultimative lösung beworben hättest, ich habe nur gesagt, dass es keine allgemeingültige Lösung ist, und das du mir darin bereits zugestimmt hast. 😉

    @~john:
    Ja. Der Destruktor darf niemals eine Ausnahme werfen, schließlich könnte der Destruktor gerade in Folge einer Ausnahme im Rahmen des Stackunwindings aufgerufen werden. Würde er nun eine Ausnahme werfen, gäbe es keine Möglichkeit mehr darauf zu reagieren. Tatsächlich würde sofort terminate() aufgerufen.


  • Administrator

    Yamakuzure schrieb:

    Ich benutze den Debugger bei Bedarf, und in diesem Fall habe ich ihn nicht gebraucht, da in diesem sehr speziellen Fall die terminate() -Ausgabe bereits sofort gezeigt hat, was schief war. Die Spezifikationen werden ab einem bestimmten Stand des Projekts auch wieder entfernt, und normalerweise benutze ich sie garnicht.

    Du entfernst die Spezifikation später sogar? Das ist doch ein riesiger Aufwand. Wieso startest du das Programm nicht immer mit dem Debugger? Während mein Programm in der Entwicklungsphase ist, starte ich es immer mit einem Debugger. Es ist eher die Ausnahme, wenn ich es ohne Debugger starte.

    Yamakuzure schrieb:

    Ich habe übrigens nie behauptet, dass du main->try->catch als ultimative lösung beworben hättest, ich habe nur gesagt, dass es keine allgemeingültige Lösung ist, und das du mir darin bereits zugestimmt hast.

    Also moment:
    1. Du hast gesagt, dass ein globales try-catch bei der main zum Verhalten von FreeOrion führt. Ich habe bereits gesagt, dass dies nicht das Problem des globalen try-catch ist, sondern dass die Entwickler hinter FreeOrion womöglich Fehler in ihrem Exception Design haben.
    2. Später hast du meine Aussage zu einem "du brauchst nur catch in main" irgendwie umformuliert. Das habe ich aber so nie gesagt, nicht in diesem Kontext und wurde auch in diesem Thread nie so erwähnt. Da nur ich allerdings etwas von catch in der main gesagt habe, habe ich dies darauf zurückgeführt, dass du meinst, ich hätte dies so gemeint.
    3. Du hast dann gesagt: "Es ist der Beweis dafür, dass die Pauschalaussage, das try-catch in main() alle Probleme pauschal löst, falsch ist.". Welche Pauschalaussage nun, wenn du behauptest, dass du nie behauptet hättest, dass ich main-try-catch als Pauschallösung für alle Probleme beworben hätte.

    ... uh, der letzte Satz gibt einem wirklich Kopfweh 🤡

    Grüssli



  • Dravere schrieb:

    1. Du solltest lesen, was Herb Sutter da geschrieben hat. Ich zitiere es gerne mal:

    Stell Dir mal vor, das habe ich nicht zum erstenmal gelesen, und jedesmal aufs neue stelle ich fest, daß das ein ziemlicher Kappes ist. Denn es werden in erster Linie Argumente wegen kaputter Compiler oder suboptimaler Implementierung angeführt, und das kann beim besten Willen kein Argument sein die Sprache abzuändern.

    Denn man kann sehr wohl einen Compiler schreiben, der erkennt, daß in einer Funktion keinerlei Code von Nöten ist, wenn in ihm ausschließlich throw() Funktionen benutzt werden. Insofern wäre dies ein Aufruf gewesen, die Compiler zu verbessern. Man will in Bezug auf throw() dieses Verhalten nun über ein anderes Sprachkonstrukt erreichen. Aber das ist schlecht. Es verkompliziert die Sprache weiter und macht alten Code kaputt (früher oder später), ohne dafür irgend einen triftigen Grund (bezüglich des Sprachdesigns) zu liefern. Schlußendlich macht man das nur, weil Compilerhersteller nicht willens sind ihre Compiler zu verbessern.

    Dravere schrieb:

    3. Ich würde aber anstatt throw() zu nehmen, doch lieber folgendes machen, falls die Unsicherheit wirklich besteht.

    Damit wird eben gerade nicht dokumentiert, daß diese Klasse bei Destruktion keine Exception wirft. Die Eigenschaft "wirft nicht bei Destruktion" ist fundamental und Eigenschaft des Interfaces. Wenn es eine Verletzung des Interfaces gibt, dann hat es keinen Sinn das Programm weiterlaufen zu lassen, da schon längst der Zustand "undefined behavior" erreicht ist.



  • Was kann denn noexcept mehr als throw() , abgesehen vom Operator?

    Und warum kann die Nothrow-Exceptionsicherheit über noexcept nicht zur Kompilierzeit erzwungen werden, indem noexcept -Funktionen keine throw -Statements enthalten dürfen und nur andere noexcept -Funktionen aufrufen können?



  • Dravere schrieb:

    ... uh, der letzte Satz gibt einem wirklich Kopfweh 🤡

    Der auch.



  • Dravere schrieb:

    Yamakuzure schrieb:

    Dravere schrieb:

    2. Man kann auch in der main ein globales try-catch machen.

    Und genau das führt dazu, dass freeorion auf meinem Rechner mit "main() caught exception(std::exception): unregistered class" nicht aussteigt, sondern endlos in der Event-Schleife weiterläuft. Mit dieser Meldung wird niemand jemals den Fehler finden ohne im geistigen "Single-Step-Modus" durchlatschen zu müssen.

    Tja, auch beim Werfen von Exceptions gilt es sinnvolle Informationen mitzuliefern. Oh Wunder, man könnte sogar Zeilennummer und File-Namen mitwerfen. Siehe die Makros __FILE__ und __LINE__ .

    Gut, ich war ungenau. Ich meinte das, was du dann geantwortet hast, dass "einfach nur" try-catch in main() nicht ausreicht. Nicht mehr, nicht weniger.

    Achja,"Fehler der FreeOrion"-Entwickler? Na, da stimme ich dir grundsätzlich zu. Die Exception kommt jedoch aus Boost, die hier zu wenig Information weiterreichen. Der Fehler der FO-Devs ist, die Ausnahme nicht da abzufangen, wo sie passiert. (Ersten beiden Boost-users, das dritte ist schon FreeOrion, und dieser Thread ist auf Platz 5.) Alles hat zwei Seiten.

    Draveres schrieb:

    Du entfernst die Spezifikation später sogar? Das ist doch ein riesiger Aufwand.

    Nein. Es sind zwei Templates, eines mit 33 und eines mit 53 Methoden die eine throw -Spezifikation hatten. Alle Methoden wurden durch mein Testprogramm mit (teils unsinnigen) Werten getestet, keine Methode wirft eine Ausnahme, die sie nicht werfen soll, und das Entfernen hat mich zwei Minuten gekostet. (Plus einer um zwei dadurch entstandener Fehler dank Zeilenumbrüche zu beheben) (*)

    Draveres schrieb:

    Während mein Programm in der Entwicklungsphase ist, starte ich es immer mit einem Debugger.

    Die Design-Phase ist bei mir grundsätzlich fertig, wenn mein UML fertig ist. Da gibts dann noch keine Zeile Code. In der Entwicklungsphase starte ich den Debugger nur, wenn es unbedingt nötig ist. Ein Programm, und ein ToolKit umso mehr, muss selber mit Fehlern umgehen können. Wenn es das dann nicht kann, ergibt das ein FreeOrion-Erlebnis. (Das Beispiel _ist_ jetzt aber doch leicht überstrapaziert, nicht wahr? ;))
    Letztendlich ist es aber Geschmackssache ob man immer im Debugger startet oder nur bei Bedarf.

    ~john schrieb:

    Denn es werden in erster Linie Argumente wegen kaputter Compiler oder suboptimaler Implementierung angeführt, und das kann beim besten Willen kein Argument sein die Sprache abzuändern.

    Da hast du natürlich Recht. In dem zitierten Artikel geht auch darum, das scheint gerne "überlesen" zu werden, dass Ausnahmespezifikation gerne mit falschen Annahmen eingebaut werden. (Ich habe den Teil oben zitiert.)
    Jedoch ist das von "Krümelkacker" beschriebene Verhalten der neuen noexcept-Operatoren das, was normalerweise (fälschlicherweise) von throw() angenommen wird, und daher freue ich mich auf die Einführung.

    Allerdings haben wir nicht 1998/99, und ich, so sehr ich mich auf C++0x freue, werde nicht mit dem gleichen Elan darangehen wie damals. War schon Krass, C++98. Endlich Exception-handling (Yay!), endlich STL (Yay!), endlich sinnvoller Umgang mit dem new operator (Yay!). Und nach dem "Draufstürzen", kam das "Niederstürzen". Die Umsetzung verlief schleppend (VC++, BorlandC++) oder garnicht (StormC++), und selbst in Quelltexten von 2003 (Kommerzell, wohlgemerkt) fand ich noch wunderschöne Konstrukte wie:

    if (!(member = new foo()))
      log_error();
    

    Das kann echt frustrierend sein... Aber ich schweife wie wild ab, was beweist, das Bremen Vs Mailand derzeit nicht sehr spannend ist. 😉

    (*)
    Spezis raus aus den Deklarationen:

    $ perl -p -i.bak -e 's/(\) |const )\s*throw\([^)]*\)(\s*)/$1$2/mig' T*.h
    

    Spezis raus aus den Definitionen:

    $ perl -p -i.bak -e '^throw\([^)]*\)\s*$//mig' T*.h
    

    Einmal astyle drüberlaufen lassen, feddich! 😃



  • Nexus schrieb:

    Was kann denn noexcept mehr als throw() , abgesehen vom Operator?

    Die Semantik ist etwas anders.

    void wirft_immer_ausnahme();
    
    void f() throw()
    {
      std::string dings = "hello";
      wirft_immer_ausnahme(); // #1
    }
    
    void f() noexcept
    {
      std::string bums = "world!";
      wirft_immer_ausnahme(); // #2
    }
    

    #1 führt dazu, dass dings ordnungsgemäß zerstört wird und anschließend std::unexpected ausgeführt wird.

    #2 führt dazu, dass std::terminate aufgerufen wird. Dabei bleibt es dem Compilerhersteller überlassen, ob bums zerstört wird. Er muss aber das Verhalten dokumentieren (implementation-defined).

    Die etwas andere Semantik von noexcept soll sich angeblich positiv auf die Laufzeit- und Speicherplatz-Effizienz auswirken (Quelle: Meine Erinnerung).

    Nexus schrieb:

    Und warum kann die Nothrow-Exceptionsicherheit über noexcept nicht zur Kompilierzeit erzwungen werden, indem noexcept -Funktionen keine throw -Statements enthalten dürfen und nur andere noexcept -Funktionen aufrufen können?

    Dann bräuchtest Du auch wieder einen Fluchtmechanismus (ähnlich wie const_cast bzgl const-correctness). Wenn Du zB genau weißt, dass eine bestimmte Bibliotheksfunktion nie eine Ausnahme wirft, sie aber nicht mit noexcept dekoriert wurde und Du sie innerhalb einer noexcept-Funktion aufrufen willst, würde das nach Deinem Vorschlag nicht mehr kompilieren. Dazu kommt noch, dass die Ausnahmespezifizierungen nur halbherzig Teil des Typsystems sind, was die Verwendung von Funktionszeigern in noexcept-Funktionen mindestens verkompliziert.

    Compile-Zeit-Meldung diesbezüglich sind Sache der Implementierungsqualität. Ein Compiler könnte zumindest eine gescheite Warnung ausgeben, wenn man so etwas baut:

    void yay() noexcept
    {
      throw 1729;
    }
    

    kk



  • krümelkacker schrieb:

    Die Semantik ist etwas anders. [...] Die etwas andere Semantik von noexcept soll sich angeblich positiv auf die Laufzeit- und Speicherplatz-Effizient auswirken.

    Vielen Dank für die Erklärung.

    krümelkacker schrieb:

    Dann bräuchtest Du auch wieder einen Fluchtmechanismus (ähnlich wie const_cast bzgl const-correctness).

    Das ist mir vorher auch in den Sinn gekommen, spontan hätte ich den Aufruf von werfenden Funktionen innerhalb von try -Blöcken mit anschliessendem catch(...) erlaubt. Ist natürlich nicht gross durchdacht. Klar, man würde die Komplexität der Sprache durch einen zusätzlichen propagierenden Sicherheitsmechanismus neben const und volatile stark erhöhen, zudem wäre bisheriger Code sozusagen nicht " noexcept -korrekt". Das ist schon nicht gerade schön.

    Auf der anderen Seite habe ich momentan wie ~john etwas das Gefühl, noexcept sei stattdessen die Halbwegs-Lösung, weil ein richtiger Mechanismus doch zu viel wäre, aber man vom bösen throw() wegkommen möchte. Ich weiss nicht, was ich davon halten soll...



  • Ich habe einen ganz einfachen Grund, keine Exception-specifications zu verwenden: Wir schreiben (auch) für MS-Compiler (vorgeschrieben von der Firma), die die Teile komplett ignorieren.

    Gruß,

    Simon2.



  • @Simon2: Das ist in der Tat ein guter Grund.

    Also ersteinmal guten Morgen, und vielfach sorry! Ich habe wirklich nicht damit gerechnet mit einer Zwischenfrage eine solche Diskussion loszutreten.

    @Gilli82: Wurde deine frage eigentlich zu deiner Zufriedenheit beantwortet?

    Und dann war da noch...
    Tut mir leid, dass ich da nicht dran gedacht habe. Es war mir ehrlich gesagt entfallen, da ich in dem von mir beschriebenen Beispiel tatsächlich einen Nutzen aus terminate() hatte.
    Es ist jedoch so, dass es einen durchaus bemerkenswerten Nutzen der Ausnahmespezifikation gibt, der bei einer nicht-gelisteten Ausnahme kein terminate() hervorruft. Nach dem, was Simon2 geschrieben hat, gehe ich aber davon aus, dass es etwas gcc-Spezifisches ist.

    Im Header "exception" von gcc-4.4.4 findet sich der folgende Schnippsel, den ich mal so zitiere:

    /** If an %exception is thrown which is not listed in a function's
       *  %exception specification, one of these may be thrown.  */
      class bad_exception : public exception 
      {
      public:
        bad_exception() throw() { }
    
        // This declaration is not useless:
        // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
        virtual ~bad_exception() throw();
    
        // See comment in eh_exception.cc.
        virtual const char* what() const throw();
      };
    

    ...bringt aber nix, wenn man bad_exception nirgendwo auffängt. 😃



  • krümelkacker schrieb:

    #1 führt dazu, dass dings ordnungsgemäß zerstört wird und anschließend std::unexpected ausgeführt wird.

    #2 führt dazu, dass std::terminate aufgerufen wird. Dabei bleibt es dem Compilerhersteller überlassen, ob bums zerstört wird. Er muss aber das Verhalten dokumentieren (implementation-defined).

    Tut mir leid, aber das neue Verhalten ist definitiv schlechter als das alte. In erster Linie muß ein Programm formal korrekt sein. Dieser ganze "undefined behavior" und "implementation defined" Scheiß braucht kein Mensch. Jetzt ein weiteres Sprachkonstrukt einzubauen, welches dieses Verhalten zeigt verschlimmbessert eindeutig die Situation.



  • ~john schrieb:

    krümelkacker schrieb:

    #1 führt dazu, dass dings ordnungsgemäß zerstört wird und anschließend std::unexpected ausgeführt wird.

    #2 führt dazu, dass std::terminate aufgerufen wird. Dabei bleibt es dem Compilerhersteller überlassen, ob bums zerstört wird. Er muss aber das Verhalten dokumentieren (implementation-defined).

    Tut mir leid, aber das neue Verhalten ist definitiv schlechter als das alte. In erster Linie muß ein Programm formal korrekt sein. Dieser ganze "undefined behavior" und "implementation defined" Scheiß braucht kein Mensch. Jetzt ein weiteres Sprachkonstrukt einzubauen, welches dieses Verhalten zeigt verschlimmbessert eindeutig die Situation.

    Ja, wenn das tatsächlich stimmt, was KK da geschrieben hat, wäre das in der Tat eine deutliche Verschlechterung der aktuellen Situation. Aktuell gibt es schließlich set_unexpected() , set_terminate() und atexit() , mit deren Hilfe man schon deutlich praktische Sachen über die Ausnahmespezifikationen machen kann. Und selbst wenn man es nicht tut, gibt es immer die Pflicht zum Stack-Unwinding. Wenn die wegfallen sollte, dann gute Nacht.

    Immerhin steht das Verhalten der Ausnahmespezifikation, wie zum Beispiel in gcc implementiert, auch so im Standard. (Siehe ISO/IEC 14882:1998, Abschnitt 15.4, Punkte 8 und 9). Den Standard zu kritisieren, weil ein bestimmter Compilerhersteller selbigen nicht umsetzen mag, geht garnicht.



  • "Deprecating Exception Specifications" (2010-03-12)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3051.html

    "Working draft, Standard for Programming Language C++" (2010-08-21)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3126.pdf



  • Also der erste Link auf ein "Proposal" ist nicht sehr nütlich. Viel interessanter finde ich da die Stellungnahmen von Standards-Meeting, März 2010:
    Report auf herbsutter.com

    Steve Adamczyk schrieb:

    We discussed two things related to exception specifications: noexcept (N3050), and deprecating exception specifications (N3051). There was some spirited discussion of what happens when you violate a promise not to throw. In the end, we decided that in such a circumstance terminate() gets called, and it’s unspecified whether any cleanup is done for local variables between the throw point and the point of the violated noexcept. It’s believed that for efficient implementations of exception handling a noexcept will not add any overhead and will not restrict optimization opportunities.

    The old-style exception specifications, including the throw() form, will now be deprecated. The library specification needs to be updated to use noexcept instead, and that won’t happen at this meeting.

    Herb Sutter schrieb:

    Note that the decision to call terminate() is not final and alternatives will be considered at future meetings. There was general (but not universal) agreement that the program should not be allowed to continue executing if a noexcept is violated because in that case the program will almost certainly be in a corrupt state, and so the consensus was in favor of guaranteeing to call terminate() instead of allowing arbitrary undefined behavior, but it’s possible that a comfortable middle ground between those two options may yet be found.

    Das PDF ist "nur" ein Working-Draft und kann, wie Herb Sutters Kommentar oben zeigt, nicht als "Garantie" bewertet werden.

    Na, bis März ist ja noch etwas hin. 😉



  • Yamakuzure schrieb:

    Das PDF ist "nur" ein Working-Draft und kann, wie Herb Sutters Kommentar oben zeigt, nicht als "Garantie" bewertet werden.

    Herb Sutter hat sich im März 2010 damit sicherlich nicht auf das/den "working draft" vom August 2010 beziehen wollen. Aktueller ist sein letzter Report, in dem es auch wieder um noexcept geht:

    noexcept part1: Destructors noexcept by default [...]
    noexcept part2: noexcept will be applied to the standard library [...]
    noexcept part3: terminate stays. It was reaffirmed that if you violate a noexcept specification, your program will call std::terminate and not continue execution in what would be a corrupt state. Stack unwinding is not required.

    Das "working draft"-PDF entspricht diesem Stand. Klar, das könnte sich immer noch ändern, aber ich denke nicht, dass das noch passieren wird.

    kk



  • Yamakuzure schrieb:

    Ja, wenn das tatsächlich stimmt, was KK da geschrieben hat, wäre das in der Tat eine deutliche Verschlechterung der aktuellen Situation.

    Nein.

    Es sind 2 komplett unterschiedliche Sachen. throw() bringt dir defakto nämlich garnichts. So oder so fliegt eine Exception die du nicht erwartest. Nur dass dir bei bei einem throw() im Gegensatz zu keiner Spezifikation die Exception Daten verloren gehen.

    Sprich: throw() bringt nie etwas. Ich kenne keine Situation wo throw() einen Vorteil gegenüber keiner Spezifikation bringt.

    noexecpt dagegen bringt etwas: der Code wird kleiner und schneller.

    noexcept ist nicht dafür da bei einem mightThrow() ein terminate() zu erzwingen. noexcept ist wie restrict in C99 oder ein __assume im VC++. Es gibt eine Garantie ab auf die man sich verlassen kann. Das ist Sinnvoll.

    Sprich:
    throw() ist niemals Sinnvoll (es gibt viele Situationen wo es nicht schadet - aber Schaden und Vorteil sind unterschiedliche Sachen).
    noexcept ist in bestimmten Situationen sinnvoll.



  • Shade Of Mine schrieb:

    throw() bringt dir defakto nämlich garnichts. So oder so fliegt eine Exception die du nicht erwartest.

    ...und vom unexpected()-Handler bearbeitet werden kann...

    Shade Of Mine schrieb:

    Nur dass dir bei bei einem throw() im Gegensatz zu keiner Spezifikation die Exception Daten verloren gehen.

    ...außer a) es wird ein verbose_terminate_handler genutzt, der diese Daten ausgibt, oder b) eine eigene bad_exception Klasse benutzt, die diese Daten weiter transportiert, oder c) ein unexpected()-Handler benutzt, der die Daten weiterverwendet, oder eine Kombination daraus.

    Shade Of Mine schrieb:

    Sprich: throw() bringt nie etwas. Ich kenne keine Situation wo throw() einen Vorteil gegenüber keiner Spezifikation bringt.

    und trotzdem sind Standardbibliotheken voll davon.

    Shade Of Mine schrieb:

    noexecpt dagegen bringt etwas: der Code wird kleiner und schneller.

    Und garantiert, dass auch wirklich keine Weiterverwendbarkeit besteht, da garantiert terminate() statt unexpected() aufgerufen wird. Außerdem garantiert es Speicherlecks. Tolle Verbesserung für was? Achja. Performance.

    Shade Of Mine schrieb:

    noexcept ist nicht dafür da bei einem mightThrow() ein terminate() zu erzwingen. noexcept ist wie restrict in C99 oder ein __assume im VC++. Es gibt eine Garantie ab auf die man sich verlassen kann. Das ist Sinnvoll.

    Nein. Je mehr ich darüber lese, desto erschütterter bin ich darüber, dass noexcept a) den Programmierer auf "throw()" und nicht-"throw()" beschränkt, und b) keinerlei Möglichkeiten irgendeiner Reaktion bietet, wie "throw()" es tut. Außerdem gibt dir "throw()" ebenfalls die Garantie ab, die du an noexcept lobst.

    Shade Of Mine schrieb:

    Sprich:
    throw() ist niemals Sinnvoll (es gibt viele Situationen wo es nicht schadet - aber Schaden und Vorteil sind unterschiedliche Sachen).
    noexcept ist in bestimmten Situationen sinnvoll.

    Nein, das sehe ich umgekehrt. Wenn die Änderungen am Standard wirklich dazu führen, dass die jetzige Flexibilität wegfällt auf eine Spezifikationsverletzung zu reagieren, dann ist noexcept, außer bei Destruktoren, niemals Sinnvoll.

    Aber als kleine Abkürzung, damit nicht die nächste, bereits getätigte, Diskussion losbricht:

    Ausnahmespezifikationen sind in den meisten Projekten unnötig. Wenn man aber ein Toolkit oder ein Framework, oder irgend etwas Anderes, was auf zur Entwicklungszeit unbekannte Weise weiterverwendet werden können soll, entwickelt, dann sieht das Anders aus.
    Dort ist es sehr wichtig, dass Funktionen und Methoden ausschließlich die Ausnahmen werfen, die Dokumentiert sind. Da Funktionen aber gerne andere Funktionen aufrufen, ist es keine Selbstverständlichkeit, die möglichen "Ketten" immer sauber dokumentiert zu bekommen.
    Während der Grundentwicklung sind nun diese Ausnahmespezifikationen recht hilfreich, denn wenn dir dein Testprogramm für eine Bibliothek um die Ohren fliegt, weil eine andere Ausnahme geworfen wird, als du dokumentiert hast, dann weißt du, dass da etwas falsch ist, und du nachbessern musst.
    Wenn du einfach alles durchlässt, werden dir und deinem ersten Kunden/Nutzer/Chef/Wemauchimmer womöglich Ausnahmen aus Bereichen, die diese nicht werfen sollten, um die Ohren fliegen. Davon ganz abgesehen, dass das sehr leicht zu einem "Unhandled Exception"-Abbruch führt, ist es sehr sehr schwer diese Stellen im Nachhinein zu finden.



  • Shade Of Mine schrieb:

    noexcept ist nicht dafür da bei einem mightThrow() ein terminate() zu erzwingen. noexcept ist wie restrict in C99 oder ein __assume im VC++. Es gibt eine Garantie ab auf die man sich verlassen kann. Das ist Sinnvoll.

    Yup.



  • Yamakuzure schrieb:

    Nein. Je mehr ich darüber lese, desto erschütterter bin ich darüber, dass noexcept a) den Programmierer auf "throw()" und nicht-"throw()" beschränkt, und b) keinerlei Möglichkeiten irgendeiner Reaktion bietet, wie "throw()" es tut. Außerdem gibt dir "throw()" ebenfalls die Garantie ab, die du an noexcept lobst.

    Nein. Wie soll ich als Compilerbauer auf die Soft-"Garantie" reagieren und auf Benutzer, die im terminate() ihre Datenbank erst wegschreiben?


Anmelden zum Antworten