Ist C++ noch zu retten?



  • Warum macht es C++ so leicht sich in den Fuß zu schießen?

    Beispiel: Type-Check zur Laufzeit. typeid bietet sich hier an. Man kann das auch einfach so benutzen und das Programm kompiliert auch problemlos, aber:

    The header <typeinfo> must be included before using typeid (if the header is not included, every use of the keyword typeid makes the program ill-formed.)

    Ok, dann inkludiert man diese beschissene Header-Datei und macht so etwas

    if (typeid(myInstance) == typeid(MyClass))
    {
         // Do something
    }
    

    Aber auch das kann schief gehen:
    There is no guarantee that the same std::type_info instance will be referred to by all evaluations of the typeid expression on the same type, although std::type_info::hash_code of those type_info objects would be identical, as would be their std::type_index.

    const std::type_info& ti1 = typeid(A);
    const std::type_info& ti2 = typeid(A);
     
    assert(&ti1 == &ti2); // not guaranteed
    assert(ti1.hash_code() == ti2.hash_code()); // guaranteed
    assert(std::type_index(ti1) == std::type_index(ti2)); // guaranteed
    

    Ja, geil!

    https://en.cppreference.com/w/cpp/language/typeid

    Hey: Man hat endlich Aggregate initialization eingeführt. - Ein bekanntes C-Feature. Juhu!
    Roundhouse-Kick in your face!

    all designators used in the expression must appear in the same order as the data members of T.

    struct A { int x; int y; int z; };
    A a{.y = 2, .x = 1}; // error; designator order does not match declaration order
    A b{.x = 1, .z = 2}; // ok, b.y initialized to 0
    

    https://en.cppreference.com/w/cpp/language/aggregate_initialization

    Tja, kompiliert aber problemlos und in C ist das erlaubt! 🤷🏻
    Weshalb führt man überhaupt ein Feature ein, wenn es so unglaublich leicht ist, sich selbst in den Fuß zu schießen?! Warum kann das C? Wieso lässt man das Feature nicht einfach weg, wenn es anders nicht geht? Ein Kollege von mir ist ein Fan von diesem Feature und ihm war nicht bekannt, dass die Reihenfolge hier eine Rolle spielt!

    Und ich rede noch nicht mal davon, dass es in C++ möglich ist etwas nicht zu returnen, obwohl die Funktionssignatur das erfordert (erst diese Woche beim Code eines Kollegen gesehen).

    Alle Beispiele hier kommen aus der Praxis, die mir beim Code Review aufgefallen sind. Es ist nicht so selbstverständlich, dass jemand die Doku so genau liest wie ich.

    Ganz ehrlich: Es wird Zeit für Sprachen wie Rust!



  • @Steffo sagte in Ist C++ noch zu retten?:

    Ganz ehrlich: Es wird Zeit für Sprachen wie Rust!

    Warum schwafelst du dann hier?



  • @manni66 Tolle, inhaltliche Antwort! Hast du dir mal CppCon-Vorträge angeschaut? Da gibt es auch jede Menge Beiträge, die C++ kritisieren.



  • @Steffo sagte in Ist C++ noch zu retten?:

    Warum kann das C?

    Weil C von Profis (=Praktikern) für Profis (=Praktiker) entwickelt wurde und nicht von Theoretikern, die immer (nach vielen "Standard"updates) noch nicht wissen, was in der Praxis wirklich gebraucht wird und was nicht.
    Weil Schlagworte wie OOP, Typsicherheit und Smartpointer in bestimmten Nicht-Techniker-Kreisen gut ankommen und suggerieren, man könne nur in C++ objektorientiert programmieren, hätte Typsicherheit und käme ohne C aus (viel besser und "sicherer" als in C).
    Und wenn mal irgendwas nicht passt, dann wird es durch die obskuren 4 Castoperatoren (durch den C++ Standard sanktioniert) wieder passend gemacht, denn der "Standard" lügt ja nicht, wenn man sich daran hält, kann nichts passieren, im Gegensatz zu C-Profis, die immer wissen was sie tun, was ein Pointer ist und wann genau man ihn casten darf und sollte.



  • @Wutz Klar. Lies dir mal die Linux-Kernel Release Notes durch. Es wimmelt nur von Speicherfehlern und Race Conditions. Von Profis geschrieben und von mehreren(!) Profis gereviewt.
    Du schreibst Märchen, Wutz. 🙂



  • @Steffo
    Du hast keine Ahnung wovon du redest. Wenn man Laien auf Profikontexte loslässt, kommt eben immer Schrott raus, d.h. auch wenn man C++ler auf C loslässt. Hier geht es um Eigenschaften der Sprache und du hast eben auch keine Ahnung, wovon du redest wenn du von C redest.



  • @Wutz Dann einige wir uns darauf, dass ich keine Ahnung habe und es vielleicht... hm..., zehn Leute auf der Welt gibt, die Ahnung haben. 🙂
    Das macht die Notwendigkeit für sichere Programmiersprachen nicht weniger relevant.



  • @Steffo
    Richtig erkannt, und einer von den Zehn hat den Linuxkernel entworfen (und allen C++ Quatsch verdammt - er weiß warum).
    Zitat H.Schmidt: "Entweder man ist Barenboim oder man spielt das Klavier nur zu Hause."
    oder etwas profaner aus meinem Mund: "Klappe halten und Finger weg, wenn man keine Ahnung hat."



  • @Steffo sagte in Ist C++ noch zu retten?:

    Warum macht es C++ so leicht sich in den Fuß zu schießen?

    Das ist doch schon immer so. Dein Thema, "Ist C++ noch zu retten?", liest sich eher so, als meintest du, dass C++ sich in die verkehrte Richtung entwickelt. (Worüber man durchaus reden kann, aber dann wohl in einem anderen Thread.)

    The header <typeinfo> must be included before using typeid (if the header is not included, every use of the keyword typeid makes the program ill-formed.)

    Was ist damit? Ich benutze nie typeid, gibts da keine Fehlermeldung? Kann ich mir nicht vorstellen, sowas ist ja für einen Compiler trivial zu detektieren, und damit kann man sich dann auch nicht mehr in den Fuß schießen.

    There is no guarantee that the same std::type_info instance will be referred to by all evaluations of the typeid expression on the same type, although std::type_info::hash_code of those type_info objects would be identical, as would be their std::type_index.

    Willst du dem Linker die Arbeit aufbürden, die ganzen type_info-Objekte aus hunderten von Objektfiles und DLLs zusammenzusuchen? Solche Forderungen kosten immer einen Haufen Aufwand und werden bei der Standardisierung niedergevotet. (Disclaimer: Ich hab nicht die Historie aufgerollt, schon gar nicht für typeid, vielleicht wars ja auch ganz anders.)

    Hey: Man hat endlich Aggregate initialization eingeführt. - Ein bekanntes C-Feature. Juhu!
    Roundhouse-Kick in your face!

    all designators used in the expression must appear in the same order as the data members of T.
    ...
    Weshalb führt man überhaupt ein Feature ein, wenn es so unglaublich leicht ist, sich selbst in den Fuß zu schießen?!

    Wie schießt du dir denn jetzt in den Fuß? Weißt du überhaupt, was die Metapher "sich in den Fuß schießen" bedeutet?

    Membervariablen haben in C++ eine festgelegte Initialisierungsreihenfolge, nämlich so wie deklariert. Wenn man dich Code Reviews machen lässt, solltest du das doch eigentlich wissen. Das ist z.B. bei Konstruktorinitialisiererlisten wichtig, dass die eben nicht in der Reihenfolge abgearbeitet werden, wie sie dastehen -- beliebter Anfängerfehler. Der Grund liegt u.a. darin, dass beim Auftreten einer Exception klar sein muss, welche Member schon konstruiert sind und welche nicht. Wenn die Reihenfolge nicht vom Typ her festgelegt wäre, sondern bei jeder Initialisierung anders sein könnte, müsste man das irgendwie zusätzlich speichern. In C spielt das alles aufgrund des anderen Objektmodells keine Rolle.

    Warum kann das C? Wieso lässt man das Feature nicht einfach weg, wenn es anders nicht geht? Ein Kollege von mir ist ein Fan von diesem Feature und ihm war nicht bekannt, dass die Reihenfolge hier eine Rolle spielt!

    Wenn man sich immer an die deklarierte Reihenfolge hält (was mir vernünftig vorkommt), muss man das ja auch nicht wissen. Aber was anderes: Hat der schon einen C++-20-Compiler? Nett.

    Und ich rede noch nicht mal davon, dass es in C++ möglich ist etwas nicht zu returnen, obwohl die Funktionssignatur das erfordert (erst diese Woche beim Code eines Kollegen gesehen).

    "trust the programmer". Es ist im Allgemeinen unmöglich festzustellen, ob ein bestimmter Codepfad jemals betreten wird, z.B.:

    extern bool foo(); // psst... gibt immer true zurück
    int bar() {
      if (foo())
        return 42;
      // ... hier ...
    }
    

    Man kann jetzt den Programmierer zwingen, ein return hinzuschreiben (und den Compiler, dafür Code zu generieren), obwohl er weiß, dass das sowieso nie ausgeführt wird, oder man kann es lassen. C++ entscheidet sich für letzteres, wie auch schon C, schon immer.



  • @Steffo sagte in Ist C++ noch zu retten?:

    @manni66 Tolle, inhaltliche Antwort! Hast du dir mal CppCon-Vorträge angeschaut? Da gibt es auch jede Menge Beiträge, die C++ kritisieren.

    Wenn man zu Rust wechseln will, erübrigt sich das.



  • @Steffo sagte in Ist C++ noch zu retten?:

    Ok, dann inkludiert man diese beschissene Header-Datei und macht so etwas
    if (typeid(myInstance) == typeid(MyClass))
    {
    // Do something
    }

    Dann mach doch sowas:

        #include <type_traits>
        if(std::is_same<MyClass0, MyClass1>::value)
        {
            // Do something
        }
    

    Zudem habe ich es so in Erinnerung, dass es normalerweise schlechtem Programmierstil entspricht, typeid/RTTI zu gebrauchen und es normalerweise eine einfachere Lösung gibt und es d. h. nur im Ausnahmefall zur Anwendung kommen sollte.

    @Bashar sagte in Ist C++ noch zu retten?:

    Membervariablen haben in C++ eine festgelegte Initialisierungsreihenfolge, nämlich so wie deklariert.

    Danke 🙏, war mir neu. Jetzt verstehe ich die Code-Analyzer-Meldung von heute.



  • @Bashar sagte in Ist C++ noch zu retten?:

    Membervariablen haben in C++ eine festgelegte Initialisierungsreihenfolge, nämlich so wie deklariert. Wenn man dich Code Reviews machen lässt, solltest du das doch eigentlich wissen. Das ist z.B. bei Konstruktorinitialisiererlisten wichtig, dass die eben nicht in der Reihenfolge abgearbeitet werden, wie sie dastehen -- beliebter Anfängerfehler.

    Der Punkt ist, weshalb ist es in C++ nicht generell ein Error, wenn die Reihenfolge nicht stimmt? Die Compiler liefern aber nur Warnings, und das meistens nur, wenn man entsprechende Flags beim Übersetzen anführt. Entweder die Norm erlaubt eine wahlfreie Initialisierung (technisch wäre das trotz Exceptions möglich – nur entsprechend aufwendiger) oder es gibt einen Error. Stattdessen eiert man in C++ herum und kann sich nicht entscheiden was man will. Die meiner Meinung nach schlechteste der möglichen Lösungen.



  • Ich finde es etwas übertrieben, eine ganze Sprache zu verteufeln, weil ein Feature etwas kurios ist.
    C++ kann viele Dinge, aber wie das ebenso mit mächtigen Werkzeugen ist, gibt es auch ein paar Fallen.

    Mit einer Waffe kann man auch ganz normal im Sportverein trainieren aber man kann auch versehentlich den Kollegen bei der Jagd übern Haufen schießen.

    Klar kannst du auf eine andere Sprache wechseln, aber da wird garantiert auch ein Moment kommen wo du dir denkst:
    "Mit C++ hätte ich das eleganter lösen können."



  • @john-0 sagte in Ist C++ noch zu retten?:

    @Bashar sagte in Ist C++ noch zu retten?:

    Konstruktorinitialisiererlisten wichtig

    Der Punkt ist, weshalb ist es in C++ nicht generell ein Error, wenn die Reihenfolge nicht stimmt?

    Tja, gute Frage, die man vielleicht Bjarne Stroustrup stellen müsste. Vielleicht hat er in Design&Evolution etwas dazu gesagt.

    Die meiner Meinung nach schlechteste der möglichen Lösungen.

    Ja, denke ich auch. Aber man kann nicht einfach in einem neuen Standard Code als falsch deklarieren, der Jahrzehnte funktioniert hat.

    Edit: Hier steht was zur Geschichte davon: https://www.usenix.org/legacy/publications/compsystems/1989/sum_stroustrup.pdf

    The syntax for initializing base classes and members has been extended to cope with multiple inheritance and the order of initialization has been more precisely defined. Leaving the initialization order unspecified in the original definition of C++ gave an unnecessary degree of freedom to language implementers at the expense of the users. In most cases, the order of initialization of members doesn't matter and in most cases where it does matter, the order dependency is an indication of bad design. In a few cases, however, the programmer absolutely needs control of the order of initialization.



  • @Steffo: Was für einen C++ Compiler hast du denn, daß da bei falscher Initialisierungsreihenfolge kein Fehler erscheint?
    Ideone-Code: gcc 8.3 (C++14)

    Zumindestens ab gcc 8.1 (neueste ist 10.1) kommt eine eindeutige Fehlermeldung,:

    <source>:5:23: error: designator order for field 'test()::A::x' does not match declaration order in 'test()::A'
         A a{.y = 2, .x = 1}; // error; designator order does not match declaration order
    

    Vorher (mit mindestens -std=c++11 als Compiler-Option):

    <source>:5:23: sorry, unimplemented: non-trivial designated initializers not supported
        A a{.y = 2, .x = 1}; // error; designator order does not match declaration order
    

    Kann man z.B. mit Compiler Explorer (godbolt) testen.
    (da es nicht kompiliert, kann ich keinen direkten Link setzen ;- )

    Edit: Und auch der msvc (ab v19.21) liefert eine identische Fehlermeldung (mit /std:c++latest):

    <source>(5): error C7560: 'x': designators must appear in member declaration order of class 'test::A'
    

    Davor kommen Syntaxfehler bzgl. des Punktes:

    <source>(5): error C2059: syntax error: '.'
    ...



  • @Bashar sagte in Ist C++ noch zu retten?:

    The syntax for initializing base classes and members has been extended to cope with multiple inheritance and the order of initialization has been more precisely defined. Leaving the initialization order unspecified in the original definition of C++ gave an unnecessary degree of freedom to language implementers at the expense of the users. In most cases, the order of initialization of members doesn't matter and in most cases where it does matter, the order dependency is an indication of bad design. In a few cases, however, the programmer absolutely needs control of the order of initialization.

    Das ist ein Plädoyer dafür die Reihenfolge gar nicht vorzuschreiben, weil nur so der Entwickler die Reihenfolge festlegen kann (indirekt macht er das ja über die Reihenfolge der Definition). Grundsätzlich hätte man vieles beim Übergang von Stroustrup C++ zu ISO C++ genauer definieren können, wenn man das gewollt hätte. Das Problem bei C und noch mehr bei C++ ist, dass die unterschiedlichen Hersteller einfach nicht wollen, dass bestimmte Sprachaspekte exakt definiert sind, weil sie das einschränkt. Das ist zum Nachteil der Nutzer der Sprache.



  • @john-0 sagte in Ist C++ noch zu retten?:

    @Bashar sagte in Ist C++ noch zu retten?:

    The syntax for initializing base classes and members has been extended to cope with multiple inheritance and the order of initialization has been more precisely defined. Leaving the initialization order unspecified in the original definition of C++ gave an unnecessary degree of freedom to language implementers at the expense of the users. In most cases, the order of initialization of members doesn't matter and in most cases where it does matter, the order dependency is an indication of bad design. In a few cases, however, the programmer absolutely needs control of the order of initialization.

    Das ist ein Plädoyer dafür die Reihenfolge gar nicht vorzuschreiben, weil nur so der Entwickler die Reihenfolge festlegen kann

    Das ist kein Plädoyer. Vielleicht solltest du dem Link folgen. Mein Auszug soll nur die geschichtliche Einordnung liefern und damit die Frage beantworten, warum die Reihenfolge der Member-Initialisierer von der tatsächlichen Initialisierungsreihenfolge abweichen darf.

    Grundsätzlich hätte man vieles beim Übergang von Stroustrup C++ zu ISO C++ genauer definieren können, wenn man das gewollt hätte.

    Der größte Nachteil von C++ ist die Rückwärtskompatibilität. Gleichzeitig ist das aber auch der größte Vorteil. Damit müssen wir wohl leben.



  • @Th69 Wir verwenden hier gcc 7.5 und frag mich nicht, was alles aktiviert wurde, damit das kompiliert.
    Es ist jedenfalls begrüßenswert, dass nun zur Compile-Zeit ein Fehler geworfen wird! 🙂



  • @john-0 sagte in Ist C++ noch zu retten?:

    Der Punkt ist, weshalb ist es in C++ nicht generell ein Error, wenn die Reihenfolge nicht stimmt? Die Compiler liefern aber nur Warnings, und das meistens nur, wenn man entsprechende Flags beim Übersetzen anführt. Entweder die Norm erlaubt eine wahlfreie Initialisierung (technisch wäre das trotz Exceptions möglich – nur entsprechend aufwendiger) oder es gibt einen Error. Stattdessen eiert man in C++ herum und kann sich nicht entscheiden was man will. Die meiner Meinung nach schlechteste der möglichen Lösungen.

    Nein bitte nicht 😕 . Wenn der Code lauffähig bleibt bitte keinen Kompilierfehler ausgeben und die Applikation nicht fertig erstellen, dann reicht eine Warnung. Ich finde es reicht sogar eine Meldung vom Code-Analyzer:

    Warning	clang-diagnostic-reorder-ctor	field 'comment' will be initialized after field 'exclude'		clang-diagnostic
    

    https://clang.llvm.org/docs/DiagnosticsReference.html#wreorder-ctor



  • @Bashar sagte in Ist C++ noch zu retten?:

    Das ist kein Plädoyer. Vielleicht solltest du dem Link folgen.

    Ich habe das Buch aus dem Regal genommen, und es darin nachgelesen.

    Mein Auszug soll nur die geschichtliche Einordnung liefern und damit die Frage beantworten, warum die Reihenfolge der Member-Initialisierer von der tatsächlichen Initialisierungsreihenfolge abweichen darf.

    Das dürfte noch wesentlich älter sein, weil es ja einmal so etwas wie C with Classes gab. Exceptions kamen erst deutlich später, so dass am Anfang die Reihenfolge wohl herzlich egal war. Aber was Stroustrup schreibt, geht ja noch über das hier angesprochene hinaus. Er spricht die Frage an, ob der Hersteller eine Compilerspezifische Reihenfolge definieren darf, die ubabhängig von der Reihenfolge der Definition im Programmcode ist. Das ist etwas anderes als wenn man in der Definition des Constructors eine andere Reihenfolge als in der Klassendefinition als Programmierer definiert.

    Der größte Nachteil von C++ ist die Rückwärtskompatibilität. Gleichzeitig ist das aber auch der größte Vorteil. Damit müssen wir wohl leben.

    Würde man das wahlfrei (für den Nutzer nicht den Compilerhersteller) machen, würde es kein Problem geben.



  • @titan99_ sagte in Ist C++ noch zu retten?:

    Nein. Wenn der Code lauffähig bleibt bitte keinen Kompilierfehler ausgeben und die Applikation nicht fertig erstellen, dann reicht eine Warnung.

    Programme mit UB sind lauffähig, und weil sie das sind gibt es doch so viel Ärger mit C++ Programmen. Entweder der Compiler findet hier Potential für UB, dann hat er den Übersetzungsvorgang abzubrechen, oder er findet das nicht und dann braucht es auch keinen Warnung mehr.


Log in to reply