PIMPL: Doppeländerung der Deklarationen umgehen?



  • Okay, danke 🙂 Das gibt mir ein paar gute Eindrücke. Dann werde ich das künftig etwas anders handhaben



  • hustbaer schrieb:

    Shade Of Mine schrieb:

    Die Zeit die du für diesen Thread aufgewendet hast, reicht für locker 2-3 Monate pimpl Deklarationen zu verdoppeln.

    Tipparbeit ersparen zu wollen ist fast immer eine schlechte Idee.

    +1

    ++1

    Ein Vorteil von pimpl ist, dass die Interfaces der inneren und äussere Klasse unterscheiden können.

    Bei der inneren Klasse mache ich ein möglichst minimales Interface, welches technisch effizient und leicht wartbar ist. In der äusseren Klasse kann ich zig helper anbieten, welche die Benutzung einfach macht.

    Natürlich ist das Tipparbeit, aber ist das Tippen wirklich das, was uns Entwicklern die meiste Zeit nimmt? Ich denke nicht.



  • Finnegan schrieb:

    z.B. Wenn man es mit unsäglichen Headern wie z.B. windows.h zu tun hat, die sich wie ein Elefant im Porzellanladen aufführen.
    Ist nicht verkehrt die direkt in einem PIMPL-Käfig einzusperren, damit sie nicht den ganzen Code verpesten (Spass mit std::numeric_limits<T>:max() , etc., etc.).

    Ich gebe dir zwar prinzipiell recht, oft helfen aber auch einzelne defines dafür: http://stackoverflow.com/questions/1394910/how-to-tame-the-windows-headers-useful-defines

    NOMINMAX verhindert zB dass die windows.h min und max definiert.



  • Natürlich ist das Tipparbeit, aber ist das Tippen wirklich das, was uns Entwicklern die meiste Zeit nimmt? Ich denke nicht.

    Tipp- und Doppelarbeit zeigt Ineffizienz und bietet Möglichkeiten Fehler zu machen. Das kostet nicht selbst Zeit, aber wenn ich merke, dass ich irgendwo ineffizient bin, habe ich automatisch den Fehler etwas "unschön" oder "unsauber" gemacht zu haben, was meine Motivation und damit meine Entwicklungsgeschwindigkeit hemmt.

    Ich widerspreche der Aussage "Tipparbeit sparen ist fast immer eine schlechte Idee" übrigens vehement.

    Denn mit dem Sparen von Tipparbeit erarbeitet man sich oft noch eine Menge anderer Vorteile. Beispielsweise nutze ich typedef s, um nicht jedes mal std::vector<MyType>::const_iterator schreiben zu müssen. Der Hauptvorteil davon ist doch - seien wir ehrlich -, dass ich Tipparbeit spare. using namespace dient auch dazu Tipparbeit zu sparen, was einige in ihren cpps anscheinend ja gar nicht nutzen, aber dann viel Spaß damit jedes mal std::placeholders::_1 zu schreiben. Ich weiß nicht, ob hier jemand Autovervollständigung nutzt, aber das dient auch nur dazu Tipparbeit zu sparen. Eine schlechte Idee? Ich denke nicht.

    Kürzerer Code mit gleichem Inhalt und sonst gleicher Lesbarkeit, kann vom Gehirn schneller verarbeitet werden, ist daher leichter zu verstehen, lässt Bugs schneller erkennen und ist auch leichter zu erweitern.

    Ich würde andersrum formulieren: Wenn man Tipparbeit sparen kann ohne dadurch andere Nachteile zu erkaufen, so sollte man das immer tun.



  • Es geht nicht um Tipparbeit. Je mehr ich tippe, desto länger wird mein Code und desto mehr muss ich beim lesen erfassen, um den Code zu verstehen. Daher ist der Grund, etwas kompakt darzustellen, die Lesbarkeit zu erhöhen und nicht, Tipparbeit zu sparen.

    Ein std::vector<MyType>::const_iterator mit einem Typedef zu definieren ist sinnvoll, da der voll qualifizierte Bezeichner doch unübersichtlich und schwer zu erfassen ist.

    Variablennamen wie a , b oder ähnliches sparen Tipparbeit. Dabei leidet aber die Lesbarkeit. Daher bevorzuge ich aussagekräftige Namen, auch wenn ich da ein wenig mehr Tippen muss.

    Doppelarbeit ist sicher ineffizient. Wer doppelte Arbeit machen muss, macht was falsch. Das sollte klar sein.

    Ich bleibe bei der Meinung, dass Tipparbeit zu sparen kein Argument für eine Programmiertechnik sein darf.



  • Das ist halt eine begriffliche Frage, aber unsere Auffassungen unterscheiden sich nach meiner Formulierung ja nicht. "a" statt einem aussagekräftigen Namen wäre ein Nachteil, den man durch Einbüßen von Tipparbeit in Kauf nehmen würde.

    Wer Doppelarbeit macht, macht was falsch? Genau das tut man bei PIMPL, wenn man Methodenaufrufe in der äußeren Klasse an die innere weiterreicht. Im Grunde erscheint es mir immer als Doppelarbeit oder ineffizient, wenn ich weite Teile copy&pasten kann/muss.

    Tipparbeit zu reduzieren heißt schlankerer Code. Wie soll der Code denn entstehen, wenn nicht durch tippen?



  • Shade Of Mine schrieb:

    Ich gebe dir zwar prinzipiell recht, oft helfen aber auch einzelne defines dafür: http://stackoverflow.com/questions/1394910/how-to-tame-the-windows-headers-useful-defines

    NOMINMAX verhindert zB dass die windows.h min und max definiert.

    Eigentlich gefällt es mir nicht so wirklich nur wegen dämlichen Makros auf eine zusätzliche Pointer-Dereferenzierung auszuweichen, allerdings war das bisher immer die effizienteste Methode ein für allemal seine Ruhe zu haben.
    Es sind ja nicht nur min und max , sondern auch noch eine Reihe andere Namen, die man gerne selbst verwenden möchte, z.B. als Variablen oder in enum s. Spass hatte ich bisher u.a. noch mit:

    far , near , small , rad1 , IN , OUT , CALLBACK , ERROR und STATUS_TIMEOUT

    Irgendwann hatte ich dann genug davon und mich für die PIMPL-Isolationshaft entschieden :D.
    Ich gebe allerdings zu, dass ein #define NOMINMAX (und andere) gefolgt von einer #undef -Orgie wahrscheinlich effizienter sind - das kann allerdings andere Nebenwirkungen haben, und Windows-Funktionen werden meist nicht so kritisch eingesetzt,
    dass die zusätzliche Dereferenzierung wirklich einen Unterschied macht.

    Finnegan



  • tntnet schrieb:

    Ein std::vector<MyType>::const_iterator mit einem Typedef zu definieren ist sinnvoll, da der voll qualifizierte Bezeichner doch unübersichtlich und schwer zu erfassen ist.

    Das is so ne Sache...

    Der ausgeschriebene Typ ist ein wenig lang. Nachteil: man muss mehr lesen.

    Dafür ist der typedef Name ein Name den man lernen muss. Nachteil: man muss mehr Namen im Kopf haben oder dauernd nachdenken/nachgucken was sich hinter dem Namen verbirgt.

    Davon abgesehen sehe ich das aber gleich wie du. 🙂



  • hustbaer schrieb:

    tntnet schrieb:

    Ein std::vector<MyType>::const_iterator mit einem Typedef zu definieren ist sinnvoll, da der voll qualifizierte Bezeichner doch unübersichtlich und schwer zu erfassen ist.

    Das is so ne Sache...

    Der ausgeschriebene Typ ist ein wenig lang. Nachteil: man muss mehr lesen.

    Dafür ist der typedef Name ein Name den man lernen muss. Nachteil: man muss mehr Namen im Kopf haben oder dauernd nachdenken/nachgucken was sich hinter dem Namen verbirgt.

    Davon abgesehen sehe ich das aber gleich wie du. 🙂

    Es ist das beste Beispiel, dass der Motivation die Lesbarkeit und nicht die Tipparbeit sein sollte. Wenn der typedef hilft, den Code lesbarer zu machen, ist er fein. Irritiert er nur, dann sollte man es lassen. Und zwar völlig egal, ob man mehr oder weniger Tippen muss.

    Da ist häufig der auto-Typ sehr praktisch. Nämlich dann, wenn der konkrete Typ der Variablen nicht wirklich relevant ist, um den Code zu verstehen.



  • tntnet schrieb:

    Es ist das beste Beispiel, dass der Motivation die Lesbarkeit und nicht die Tipparbeit sein sollte.

    Exakt.
    Tipparbeit ist so gut wie nie ein Argument dass beim Programmieren zählen darf.

    Um Tipparbeit zu sparen haben wir mächtige Tools die uns Autovervollständigung, Vorlagen, etc. ermöglichen. Wenn man aber überlegt: soll ich den Code so oder so schreiben, dann darf nie die Tipparbeit ein Argument sein.

    Man muss auch bedenken: wieviel Prozent der Zeit verbringt man mit reinem Tippen? 10%? 5%? weniger?



  • Finnegan schrieb:

    Pointer-Dereferenzierung

    Man kann das selbe auch ohne Pointer-Dereferenzierung machen, man muss dazu nur sizeof() und alignof() kennen. 😉
    Is aber - wenn man sich die nötigen Helper-Templates selbst schreiben muss - a bisserl a Aufwand.

    Finnegan schrieb:

    Irgendwann hatte ich dann genug davon und mich für die PIMPL-Isolationshaft entschieden :D.
    Ich gebe allerdings zu, dass ein #define NOMINMAX (und andere) gefolgt von einer #undef -Orgie wahrscheinlich effizienter sind - das kann allerdings andere Nebenwirkungen haben, und Windows-Funktionen werden meist nicht so kritisch eingesetzt,
    dass die zusätzliche Dereferenzierung wirklich einen Unterschied macht.

    Also #include-creep ist schon ne krasse Sache.
    Ich seh das hier bei unseren Projekten, die quasi PIMPL-frei entwickelt wurden.

    Mit windows.h kommt man noch klar, auch wenns immer wieder nervt. Wobei 100% PIMPL-frei sind wir auch nicht. Manche thirdparty Libs vertragen sich einfach nicht mit dieser oder jener anderen Header, und dann muss man irgendwas machen - was dann meist PIMPLn heisst.

    Was aber wirklich nervt ist eben der unglaubliche #include-creep der sich so ansammelt. Du willst eigentlich nur auf irgend eine Helper-Klasse zugreifen, und auf einmal hast du 50 Boost Headers, 10 Crypto++ Headers, noch etliche Xerces Headers und fast das ganze Windows SDK reingezogen. Die Build-Zeiten sehen dann entsprechend aus. Und Precompiled-Headers helfen da auch nur sehr bedingt. Wobei die Zeit für nen kompletten Rebuild nichtmal das schlimmste ist. Viel schlimmer ist es mMn. dass inkrementelle Builds dadurch oft unglaublich langsam werden.

    Also so verkehrt finde ich PIMPL nicht.



  • hustbaer schrieb:

    Was aber wirklich nervt ist eben der unglaubliche #include-creep der sich so ansammelt. Du willst eigentlich nur auf irgend eine Helper-Klasse zugreifen, und auf einmal hast du 50 Boost Headers, 10 Crypto++ Headers, noch etliche Xerces Headers und fast das ganze Windows SDK reingezogen. Die Build-Zeiten sehen dann entsprechend aus. Und Precompiled-Headers helfen da auch nur sehr bedingt.

    Ich freue mich schon auf den Zeitpunkt, wenn wir über diesen altmodischen Quark nur noch lachen können und einfach schreiben:

    import std.io;
       import boost.super_cool;
       import windows.cant_do_it_without_it;
    

    ... und die Datei dann genau so schnell gebaut wird wie ein "Hallo, Welt"-Programm 😃

    Finnegan (Modules-Fan, auch wenns ein bissl wie Java aussieht)



  • Amen.



  • Jopp. Wobei ich nicht damit rechne dass das in diesem Jahrzehnt noch kommt.


  • Mod

    hustbaer schrieb:

    Jopp. Wobei ich nicht damit rechne dass das in diesem Jahrzehnt noch kommt.

    😕 Wie kommste darauf?



  • hustbaer schrieb:

    Manche thirdparty Libs vertragen sich einfach nicht mit dieser oder jener anderen Header

    Jo, zum Beispiel weil die thirdparty Libs alle die selbe andere thirdparty Lib benutzen aber in verschiedenen Versionen.
    Da kriegste aber auch ohne Header Ärger, z.B. wenn die Oracle-CLient-Lib-DLL Funktionen von Kerberos exportiert.

    hustbaer schrieb:

    Was aber wirklich nervt ist eben der unglaubliche #include-creep der sich so ansammelt. Du willst eigentlich nur auf irgend eine Helper-Klasse zugreifen, und auf einmal hast du 50 Boost Headers, 10 Crypto++ Headers, noch etliche Xerces Headers und fast das ganze Windows SDK reingezogen. Die Build-Zeiten sehen dann entsprechend aus.

    Kann ich nicht nachvollziehen.



  • Arcoth schrieb:

    hustbaer schrieb:

    Jopp. Wobei ich nicht damit rechne dass das in diesem Jahrzehnt noch kommt.

    😕 Wie kommste darauf?

    Wie ich darauf komme: weil es super überhaupt gar nicht trivial ist.
    Ich hab' aber nicht verfolgt für was es schon konkrete, gut ausgearbeitete Proposals gibt und für was nicht.
    Ich hab nur gelesen dass es für C++17 nicht fix vorgesehen ist. Und wenn man mal annimmt dass es sich für nicht ausgeht, dann wird es mit 1x halt schon eng. Ich rechne halt kaum damit dass der Standard nach C++17 schon 18 oder 19 rauskommen wird.

    Wäre aber natürlich cool wenn es sich trotzdem noch ausgeht.



  • volkard schrieb:

    hustbaer schrieb:

    Was aber wirklich nervt ist eben der unglaubliche #include-creep der sich so ansammelt. Du willst eigentlich nur auf irgend eine Helper-Klasse zugreifen, und auf einmal hast du 50 Boost Headers, 10 Crypto++ Headers, noch etliche Xerces Headers und fast das ganze Windows SDK reingezogen. Die Build-Zeiten sehen dann entsprechend aus.

    Kann ich nicht nachvollziehen.

    Is jetzt ein bisschen unspezifisch, nen?



  • hustbaer schrieb:

    volkard schrieb:

    hustbaer schrieb:

    Was aber wirklich nervt ist eben der unglaubliche #include-creep der sich so ansammelt. Du willst eigentlich nur auf irgend eine Helper-Klasse zugreifen, und auf einmal hast du 50 Boost Headers, 10 Crypto++ Headers, noch etliche Xerces Headers und fast das ganze Windows SDK reingezogen. Die Build-Zeiten sehen dann entsprechend aus.

    Kann ich nicht nachvollziehen.

    Is jetzt ein bisschen unspezifisch, nen?

    Bei mir sammelte sich noch nie #include-creep an. Habe aber Nachbarprojekte gesehen, die das Problem hatten. Ich nahm immer an, daß sie bloß sparsamer includen müßten, dann wäre das Problem weg. Und vielleicht zur Not gelegentlich einen kleinen Trick anwenden, aber sicher nicht chronisch zu pimpln.
    Ich schreibe aber schon mal gerne "#ifdef WINDOWS typedef void* os::File;" oder sowas, um plattformunabhängig zu werden und zugleich die <windows.h> aus dem Header los zu sein.



  • Gut, Tipparbeit war kein gut gewähltes Wort.

    Doppelarbeit zu vermeiden und Code zu wiederholen finde ich jedoch durchaus schlecht. Und wenn ich eben von außen nach innen viel Zeug weiterreiche und das private Interface dann zu 95% eine Untermenge des öffentlichen Interfaces ist, habe ich schon den Eindruck, dass hier was schief gelaufen ist.

    Ändere ich bei einer Durchreich-Methode einen Parameter, muss ich das an vier Stellen ändern. Das kann's doch nicht sein.

    Lösungsvorschläge dafür wurden hier ja aber angeboten, ihr könnt also eure Side-Discussion gerne weiterführen. Für mich ist der Thread erledigt. 😋


Anmelden zum Antworten