Verleitet die Mächtigkeit der Sprache C++ dazu es kompliziert zu machen?



  • std library und boost verleiten zum komplizierten



  • Viele Sachen kann man mit den sprachlichen Mitteln viel einfacher ausdruecken als in anderen Sprachen, aber es passiert mir schon oefters, dass ich etwas von vornherein generisch mache, wenn es ueberhaupt nicht noetig ist und es nur eine Instanziierung ueberhaupt gibt und ich mich dann mit schwierigen Template-ausdruecken und fuerchterlichen Fehlermeldungen herumschlagen darf.



  • Es kommt auf die Probleme an, die man lösen will. Z.B. habe ich wegen RAII in C++ weniger Fehlerquellen, als z.B. in Java (mit dem ich täglich zu tun habe).
    Dann gibt es aber Probleme, die ich in Java einfacher lösen kann, und es trotzdem funktioniert.

    Als ich damals von TMP erfahren hatte, versuchte ich krampfhaft TMP einzusetzen oder zumindest Probleme dafür zu finden. Das ist natürlich total bekloppt. Weil es mich nicht weiter gebracht hatte.

    Disziplin ist es nicht, die man braucht. Man braucht sehr viel Selbstbewusstsein und Ar*** in der Hose, um drüber zu stehen, das man halt etwas in C++ einfach einfach gelöst hat.



  • volkard schrieb:

    Vermutlich meinste
    http://www.ariel.com.au/jokes/The_Evolution_of_a_Programmer.html

    Das ist Köstlich.
    Ja in der Richtung "overengineered" könnte man das auch betrachten.



  • Ich finde dass SFML ein gutes Beispiel für schönes C++ ist. Klar und sauber.



  • Ich denke vor allem TMP und Boost verleiten Neulinge zum overengineering.



  • std__boost schrieb:

    std library und boost verleiten zum komplizierten

    Dieser Post ist nicht ausser Acht zu lassen. Statt einer einfachen for -Schleife tendiert man zu std::generate und irgendeinem lokalen Funktor oder einem Lambda. Lesbarer oder "besser" finde ich das nicht. Wobei ich gestehen muss, dass ich als Hobby-Programmierer auch dazu tendiere, kleine Dinge fürchterlich zu overengineeren. Zumindest macht es Spass. 🕶



  • asfdlol schrieb:

    std__boost schrieb:

    std library und boost verleiten zum komplizierten

    Dieser Post ist nicht ausser Acht zu lassen. Statt einer einfachen for -Schleife tendiert man zu std::generate und irgendeinem lokalen Funktor oder einem Lambda. Lesbarer oder "besser" finde ich das nicht.

    "Effective STL" ist ein Buch, wo der Autor einem Seite um Seite einhämmert, daß man ja keine einfachen Schleifen mehr benutzen darf.



  • Tyrdal schrieb:

    Ich denke vor allem TMP und Boost verleiten Neulinge zum overengineering.

    Das liegt aber eher am Neuling als an Boost.

    Boost selber ist "over-engineered", damit man das als Benutzer von Boost nicht machen muss.

    asfdlol schrieb:

    std__boost schrieb:

    std library und boost verleiten zum komplizierten

    Dieser Post ist nicht ausser Acht zu lassen. Statt einer einfachen for -Schleife tendiert man zu std::generate und irgendeinem lokalen Funktor oder einem Lambda. Lesbarer oder "besser" finde ich das nicht

    Ich sehe das ganz anders. Statt einem komplizierten for -Statement tendiere ich zu fertigen Algorithmen wie generate . Bei find , remove und partition ist der Vorteil offensichtlicher.



  • TyRoXx schrieb:

    Bei find , remove und partition ist der Vorteil offensichtlicher.

    Bei find will man üblicherweise die Findeposition weiterverarbeiten, da bietet sich eine Funktion an. Die hat nen guten Zweck und nen passenden Namen. remove und partition auch. generate ist schon gelegentlich fraglich.
    Bevozugst Du auch die Ausgabe als copy auf ostream-Iteratoren?



  • TyRoXx schrieb:

    Tyrdal schrieb:

    Ich denke vor allem TMP und Boost verleiten Neulinge zum overengineering.

    Das liegt aber eher am Neuling als an Boost.

    Tja der Neuling denkt halt das muss so und das ist meist nicht der Fall.



  • Worin liegt bei dieser Frage das Problem?

    Mit Ausnahme von ASSEMBLER und vielleicht noch des alten BASIC ist jede
    Programmiersprache in ihren Möglichkeiten komplex. Solange man nicht alles
    voll versteht oder braucht, benutzt man es nicht. Oder anders gesagt, man
    verwendet die komplexeren Dinge erst dann, wenn man darin einen Vorteil sieht.

    Als Führerscheinneuling setze ich mich auch nicht gleich in einen Formel1-Rennwagen!



  • volkard schrieb:

    Bevozugst Du auch die Ausgabe als copy auf ostream-Iteratoren?

    Ich verwende zwar möglichst selten ostream . Aber wenn ich das mache, dann gerne mit Iteratoren.

    Verwirrte Anfänger gibt es immer.

    Die Mächtigkeit von C++14 verleitet mich persönlich dazu es richtig zu machen ("braucht man die Indirektion hier wirklich?"). Ich glaube, dass ich inzwischen einschätzen kann, wann das zu kompliziert wird.
    In anderen Sprachen, zum Beispiel C#, mache ich mir viel weniger Gedanken über Kleinkram. C# bietet weniger Spielraum für "clevere" Lösungen. Man braucht die auch nicht, weil sowieso alles ineffizient ist.



  • berniebutt schrieb:

    Worin liegt bei dieser Frage das Problem?

    Solange man nicht alles voll versteht oder braucht, benutzt man es nicht. Oder anders gesagt, man verwendet die komplexeren Dinge erst dann, wenn man darin einen Vorteil sieht.

    Das Problem ist, dass man Vorteile sieht, die gar nicht da sind. Z.B. hat man eine Funktion und anstatt sie einfach so zu schreiben, bietet man sie fuer integer und float an, wobei man jeweils mit enable_if und is_integral und so dafuer sorgt, dass die richtige Ueberladung getroffen wird. Man schlaegt sich dann mit templates und unverstaendlichen Fehlermeldungen herum und schliesslich hat man eine wundervolle generische Funktion mit Sicherheitschecks und allem, nur um am Ende festzustellen, dass man dafuer eine Stunde Arbeit gebraucht hat und ein einfacher Overload fuer double und int voellig ausreicht, aber in 2 Minuten fertig gewesen waere.



  • TyRoXx schrieb:

    In anderen Sprachen, zum Beispiel C#, mache ich mir viel weniger Gedanken über Kleinkram. C# bietet weniger Spielraum für "clevere" Lösungen. Man braucht die auch nicht, weil sowieso alles ineffizient ist.

    Jo, geht mir auch so.

    Heißt aber nicht unbedingt, daß die Programme in Java/C# dann lesbarer werden. Da passieren auf magische Weise manchmal so Dinge wie class ObjectManager, dependency inversion, tiefe Kopien mithilfe eines XML-Serielizers,
    http://www.c-plusplus.net/forum/327984-full



  • volkard schrieb:

    asfdlol schrieb:

    std__boost schrieb:

    std library und boost verleiten zum komplizierten

    Dieser Post ist nicht ausser Acht zu lassen. Statt einer einfachen for -Schleife tendiert man zu std::generate und irgendeinem lokalen Funktor oder einem Lambda. Lesbarer oder "besser" finde ich das nicht.

    "Effective STL" ist ein Buch, wo der Autor einem Seite um Seite einhämmert, daß man ja keine einfachen Schleifen mehr benutzen darf.

    Und womit wird das begründet?



  • Questionmark schrieb:

    volkard schrieb:

    "Effective STL" ist ein Buch, wo der Autor einem Seite um Seite einhämmert, daß man ja keine einfachen Schleifen mehr benutzen darf.

    Und womit wird das begründet?

    Die Funktionen aus <algorithm> sind wegabstrahierter Programmfluss.
    Wenn man eine Schleife selbst schreibt, z.B. so:

    vector<int> result;
    for (int i : v)
    {
        if (i % 2 == 0)
            result.push_back(i);
    }
    

    reicht es nicht, den Schleifenkopf zu lesen um zu verstehen, was passiert. Man muss den ganzen Schleifenkörper, der ja durchaus länger als hier sein kann, anschauen.

    Wenn man allerdings Funktionen aufruft, die diese Schleifen für einen selbst intern umsetzen, erkennt man direkt, was grob passiert.

    vector<int> result;
    copy_if(begin(v), end(v), back_inserter(result), [](int i)
    {
        return i % 2 == 0;
    });
    

    Hier im Beispiel weiß ich direkt einiges, z.B. dass manche Elemente von v nach result kopiert wird, dass in result nachher nichts drin ist, was vorher nicht in v war, dass die Länge von result <= der Länge von v sein wird ...
    Zusätzlich vermeidet man eventuelle Dusseligkeitsbugs wie Endlosschleifen, ungültige Iteratoren usw.

    Der code wird selbstdokumentierender, und man programmiert einfach auf einer höheren Abstraktionsebene.
    Das kann noch weitere Vorteile haben, wie beispielsweise den, dass man irgendwann einfach sein copy_if durch ein hypothetisches copy_if_multithread oder sowas ersetzen kann.

    Klar kann es auch situationen geben, in der dieser funktionale Stil die Lesbarkeit nicht verbessert sondern verschlechtert, oder man aus anderen Gründen besser zur weniger abstrakten Lösung greift. Das muss man dann abwägen und ist auch ein Bischen Frage der persönlichen Vorlieben.

    Die Syntax für sowas ist in C++ vielleicht nicht die schönste, aber mein persönlicher Default ist trotzdem auch hier dieser funktionale Stil.



  • Dobi schrieb:

    Questionmark schrieb:

    volkard schrieb:

    "Effective STL" ist ein Buch, wo der Autor einem Seite um Seite einhämmert, daß man ja keine einfachen Schleifen mehr benutzen darf.

    Und womit wird das begründet?

    Die Funktionen aus <algorithm> sind wegabstrahierter Programmfluss.
    Wenn man eine Schleife selbst schreibt, z.B. so:

    vector<int> result;
    for (int i : v)
    {
        if (i % 2 == 0)
            result.push_back(i);
    }
    

    reicht es nicht, den Schleifenkopf zu lesen um zu verstehen, was passiert. Man muss den ganzen Schleifenkörper, der ja durchaus länger als hier sein kann, anschauen.

    Wenn man allerdings Funktionen aufruft, die diese Schleifen für einen selbst intern umsetzen, erkennt man direkt, was grob passiert.

    vector<int> result;
    copy_if(begin(v), end(v), back_inserter(result), [](int i)
    {
        return i % 2 == 0;
    });
    

    Hier im Beispiel weiß ich direkt einiges, z.B. dass manche Elemente von v nach result kopiert wird, dass in result nachher nichts drin ist, was vorher nicht in v war, dass die Länge von result <= der Länge von v sein wird ...
    Zusätzlich vermeidet man eventuelle Dusseligkeitsbugs wie Endlosschleifen, ungültige Iteratoren usw.

    Der code wird selbstdokumentierender, und man programmiert einfach auf einer höheren Abstraktionsebene.

    Nicht wirklich. Ich wette 100% der Leute die nicht sehr viel mit copy_it und lambdas arbeiten können die for schleife schneller lesen. 99.9% der Leute die sehr viel mit copy_it und lambdas arbeiten können die for schleife schneller lesen. 0.1% der Leute die sehr viel mit copy_if und lambdas arbeiten können die for schleife und das copy_if ding gleich schell lesen. Außderdem hast du es auch noch so formatiert, dass es beim schnellen drüberlesen wie eine Funktion aussieht die i % 2 == 0 zurückgibt.

    Das Argument mit Dusseligkeitsbugs wie Endlosschleifen und ungültige Iteratoren spricht seit for-each-schleifen wohl eher für die for-each-schleife als für irgendwelche iteratoren die man an ein copy_if übergeben muss.

    Dobi schrieb:

    Das kann noch weitere Vorteile haben, wie beispielsweise den, dass man irgendwann einfach sein copy_if durch ein hypothetisches copy_if_multithread oder sowas ersetzen kann.

    parallel for, openmp...



  • nenene... schrieb:

    Außderdem hast du es auch noch so formatiert, dass es beim schnellen drüberlesen wie eine Funktion aussieht die i % 2 == 0 zurückgibt.

    Und bei

    vector<int> result;
    for (int i : v)
    {
        if (i % 2 == 0)
            result.push_back(i);
    }
    

    macht auch nur die Formatierung einen leichten Lesestolpler. Also einmal Klammern und einmal nicht, obwohl beide mal nur ein Befehl behandelt wird. Die Klammern scheinen mir das if irgendwie von der Schleife zu entfernen.

    vector<int> result;
    for (int i : v)
        if (i % 2 == 0)
        {
            result.push_back(i);
        }
    

    nenene... schrieb:

    Das Argument mit Dusseligkeitsbugs wie Endlosschleifen und ungültige Iteratoren spricht seit for-each-schleifen wohl eher für die for-each-schleife als für irgendwelche iteratoren die man an ein copy_if übergeben muss.

    Vorher war das Muster der for-Schleife, die über einen ganzen Container läuft, eigentlich auch so eingebrannt, daß man es fast wie Schlüsselwort gelesen hat. Jo, mit den endlich existierenden for-each-Schleifen wird einiges besser.



  • @nenene...:
    Na gut, dazu wieviel Prozent der Leute momentan das eine oder das andere besser lesen können, weiß ich nicht. Ich gehör zumindest zu der Gruppe, die du gar nicht aufgezählt hast, nämlich zu der, die das copy_if schneller lesen können. 😉
    Ist halt Gewöhnungssache. Und wenn man überall sowas benutzt, ist das return auch nicht mehr überraschend.

    Mit for-each-Schleifen kann man copy_if natürlich nachbauen, aber das kann man auch mit for(auto it ...) oder while oder goto .
    Mir gefallen die zusätzlichen Garantien, die ich durch copy_if bekomme halt sehr gut.

    volkard schrieb:

    Vorher war das Muster der for-Schleife, die über einen ganzen Container läuft, eigentlich auch so eingebrannt, daß man es fast wie Schlüsselwort gelesen hat.

    Vielleicht ist irgendwann ja der algo-funktion-plus-lambda-stil auch so eingebrannt, dass einem ein for-each wiederum komisch vorkommt wenn es nicht gebraucht wird, ebenso wie einem in funktionalen Sprachen ein fold doof vorkommt wenn es ein map oder ein filter auch getan hätte. Ich hoffe es zumindest. 😉


Anmelden zum Antworten