#ifdef -> einrücken oder nicht?



  • ja hallo liebes forum,

    ich bin gerade mit dem philosophischen Problem beschäftigt ob man #ifdefs einrücken soll oder nicht. Es kann doch vorkommen dass ich eine zeile habe die in einer 4 oder 6 einrückung vorkommt. Wenn ich das #ifdef ganz vorne platziere sieht es seltsam aus. Wie macht ihr das? Rückt ihr dann das #ifdef komplett auch ein?

    Danke für Antworten...



  • Ich habe nur #ifndef's als Guards, die werden nicht eingerueckt.



  • gruppa schrieb:

    ich bin gerade mit dem philosophischen Problem beschäftigt ob man #ifdefs einrücken soll oder nicht. Es kann doch vorkommen dass ich eine zeile habe die in einer 4 oder 6 einrückung vorkommt.

    Das ist wirklich philosophisch. Wobei ich in einen Punkt etwas skeptisch bin "4 oder 6 einrückung" deutet für mich auf Code hin, der besser aufgesplittet gehört. Zudem (aber das ist meine Meinung hierzu) versuche ich ifdefs möglichst nur auf unterster Ebene zu verwenden oder in mit dem Code auszulagern.



  • knivil schrieb:

    Ich habe nur #ifndef's als Guards, die werden nicht eingerueckt.

    Kein Debug Code?
    Kein Plattformabhängiger Code?

    O_o

    ich rücke bis auf guards schon ein...


  • Administrator

    Solche Präprozessor Verzweigungen starten bei mir immer von vorne mit der Einrückung. Es sieht zwar hässlich aus, aber genau das will ich, damit man es ganz klar sieht. Einzig was ich mache, ist dann eine Einrückung in den Präprozessor Anweisungen. Also kurzes Beispiel:

    void foo()
    {
        // bla bla bla ...
        int var = 0;
        // usw.
    
        if(anweisungGleichTrue)
        {
    #ifdef LINUX
    #   define KILL_YOURSELF
           // anderer Code
    #else
    #   define WHAT_EVER
           // wieder anderer Code
    #endif
        }
    
       // Und weiter gehts.
    }
    

    Im allgemeinen probiere ich allerdings sowieso so stark wie möglich darauf zu verzichten und löse dies ausserhalb über Templates oder spezielle Klassen. Ich habe dann also um eine Klasse herum oder um ein typedef oder was auch immer, eine Präprozessor Verzweigung. Ich probiere sie also vom Code zu trennen:

    #ifdef BLIBLABLUB
    #  define WINDOWS_SYSTEM
    
    class BliBlaBli
    {
      int handle;
      // ...
    };
    
    #else // ifdef BLIBLABLUB
    #  define OTHER_SYSTEM
    
    class BliBlaBli
    {
      void* handle;
      // ...
    };
    
    #endif // ifdef BLIBLABLUB
    

    Grüssli



  • Debugcode schon, aber auch nicht mehr als ein "print". Plattformabhaengig? ... Eigentlich nicht, programmiere selten Fenster etc. Wenn doch plattformabhaengig, dann sind die Klassen ausgelagert und das Einbinden der Implementationen wird ueber das makefile gesteuert. Meist sollen meine Anwendungen auch nur unter Unixen laufen, da braucht man recht wenig WSAStartup etc. OpenGL-Initialisierung uebernimmt SDL ...



  • Ah - interessant. Danke euch. Ja mit den 4 oder 6 einrückungen ist wohl war dass man splitten sollte. Ich werde das überdenken.

    Das letztere von dravere mit dem auslagern - das geht doch nicht immer oder? Ich kann doch nicht jede einzelne #ifdef anweisung auslagern oder verstehe ich dich falsch?



  • Dravere schrieb:

    void foo()
    {
        // bla bla bla ...
        int var = 0;
        // usw.
    
        if(anweisungGleichTrue)
        {
    #ifdef LINUX
    #   define KILL_YOURSELF
           // anderer Code
    #else
    #   define WHAT_EVER
           // wieder anderer Code
    #endif
        }
    
       // Und weiter gehts.
    }
    

    *brr*

    #ifdef nie innerhalb von code. alles schön sauber trennen bitte.
    idealerweise so, dass man ein

    #include "foo.hpp"
    macht und in foo.hpp ist die verzweigung und je nachdem wird eben win32/foo.hpp oder posix/foo.hpp inkludiert.

    so dass man nie #ifdef und code mischt...



  • Shade Of Mine schrieb:

    #ifdef nie innerhalb von code. alles schön sauber trennen bitte.
    idealerweise so, dass man ein

    #include "foo.hpp"
    macht und in foo.hpp ist die verzweigung und je nachdem wird eben win32/foo.hpp oder posix/foo.hpp inkludiert.

    so dass man nie #ifdef und code mischt...

    Manchmal kommt man nicht drumrum, wenn man nicht grade alles doppelt und dreifach tippen will. Zum Beispiel:

    template <typename T> class empty_base {
    
    // empty base class optimization bug with GCC 3.0.0
    #if defined(__GNUC__) && __GNUC__==3 && __GNUC_MINOR__==0 && _GNU_PATCHLEVEL__==0
      bool dummy; 
    #endif
    
    };
    

    (aus boost::operators)



  • pumuckl schrieb:

    Manchmal kommt man nicht drumrum, wenn man nicht grade alles doppelt und dreifach tippen will. Zum Beispiel:

    Das ist zB ein schönes Beispiel wie man es nicht machen soll.
    Zumindest ein

    #ifdef BOOST_EMPTY_BASE_NEEDS_BOOL
    bool dummy;
    #endif

    wäre deutlich sinnvoller gewesen.

    Die boost librariers sind oft ziemlich häßlich wenn man näher hinsieht.


  • Administrator

    Shade Of Mine schrieb:

    *brr*

    Ich habe ja gesagt, dass ich es im allgemeinen auslagere. Finde das auch äusserst *brr* 😃
    Aber man kommt halt nicht ganz immer drum herum oder der zusätzliche Aufwand ist unverhältnismässig höher.

    Am liebsten aber immer noch nur ein typedef oder ein include verändern, definitiv!

    Grüssli



  • 😕
    eh jetzt bin ich total verwirrt.

    Wie soll man denn z.B. sowas auslagern:

    Class::method()
    {
       int a;
    
    #ifdef SHMEM
    #   define WORLD NULL
        a = library_call(...);
    #else
        a = 1;
    #endif  
    
      // rechne mit a weiter welches entweder über SHMEM gesetzt ist oder nicht
    
    }
    


  • Naja, man kann ein Shared memory object (sofern SHMEM das bedeutet) und seine Verwendung in einer Klasse kapseln. Somit sind die Implementationsdetails fuer den Benutzer deines Objekts verborgen.



  • es geht jetzt nicht unbedingt um shared memory - kann auch ein anderes flag sein aber ums prinzip gehts mir....wie soll ich denn einzelne anweisungen kapseln wenn der nachfolgende code davon abhängt?



  • gruppa schrieb:

    es geht jetzt nicht unbedingt um shared memory - kann auch ein anderes flag sein aber ums prinzip gehts mir....wie soll ich denn einzelne anweisungen kapseln wenn der nachfolgende code davon abhängt?

    In eine Funktion packen:

    int a = get_a();
    

    und fertig. Wo ist das Problem?

    PS:
    einfach mal daran denken was passiert wenn es plötzlich mehr flags gibt.
    #ifdefs im code sind tödlich wenn sich einmal etwas daran ändern muss..


  • Administrator

    Und vielleicht um die Sache von Shade Of Mine noch ein wenig zu ergänzen:

    implA.hpp

    inline int get_a(...) { return lib_call(...); }
    

    implB.hpp

    inline int get_a(...) { return 1; }
    

    realImpl.cpp

    #ifdef XYZ
    #  include "ImplA.hpp"
    #else
    #  include "ImplB.hpp"
    #endif
    
    // Irgendwo ...
    int a = get_a(...);
    

    Das ist jetzt sehr vereinfacht, aber es soll ja nur das Prinzip aufzeigen 😉
    Man kann das dann eben auch mit Basisklassen, veränderten typedefs auf unterschiedliche Klasse oder veränderte Templateparameter machen. Sie müssen halt jeweils nur die gleiche Schnittstelle haben, aber das ist meistens kein Problem.

    Grüssli



  • Ich wuerde es nicht inline machen, da ich dann wieder ifdef's brauche, um die richtigen Header zu inkludieren. Ausserdem mag ich inline nicht, da es die Implementation nicht versteckt, es traegt meist auch nicht zur Steigerung der Gesamtperformance bei.



  • ok...ich folge euch ein wenig brauche aber noch eure Hilfe damit ich es wirklich verstanden habe:

    1. Heißt das jetzt dass ihr immer ein #include "Impl*.hpp" macht wann immer ihr
      ein #ifdef habt? Das bläht den code doch immens auf oder nicht?

    2. Impl*.hpp ist das eine Klasse die die einzelnen Methoden zum Setzen der Parameter bzw. der Berechnungsmethoden liefert? Nur ein Header kanns ja nicht sein oder?

    3. Mit dieser Technik werden ja fast alle Methoden die speziell flag-abhängig sind in einer Impl datei landen. Angenommen ich habe parallele Kommunikation über OpenMP oder einen anderen Standard. Und ich will jetzt entweder Kommunikation zwischen prozessen oder eben nicht. Wie soll ich hier kapseln? Eine Methode komm() die eigentlich erst in den Impl* implementiert wird?
      Damit muss ich ja doch sämtlichen code in die Impl* dateien auslagern bzw. für jede unterschiedliche Zuweisung eine eigene Methode in der IMpl* datei...

    Mir fehlt immer noch ein spezieller Bezug...

    Danke euch 🙂



  • knivil schrieb:

    Ich wuerde es nicht inline machen, da ich dann wieder ifdef's brauche, um die richtigen Header zu inkludieren. Ausserdem mag ich inline nicht, da es die Implementation nicht versteckt, es traegt meist auch nicht zur Steigerung der Gesamtperformance bei.

    versteckt ist es schon.

    und es soll ja nur die idee aufzeigen - das ganze geht mit .cpp Dateien uebrigens genauso.

    gruppa schrieb:

    1. Heißt das jetzt dass ihr immer ein #include "Impl*.hpp" macht wann immer ihr
      ein #ifdef habt? Das bläht den code doch immens auf oder nicht?

    Nein, ich mach ein
    #include "xyz.hpp"
    und xyz.hpp sieht so aus:

    #ifdef BLA
    #  include "bla/xyz.hpp"
    #else
    #  include "nonbla/xyz.hpp"
    #endif
    

    im client code selber sieht man kein einziges #ifdef! das ist das essentielle.

    1. Impl*.hpp ist das eine Klasse die die einzelnen Methoden zum Setzen der Parameter bzw. der Berechnungsmethoden liefert? Nur ein Header kanns ja nicht sein oder?

    Verstehe ich nicht.
    Es ist ein Header mit spezialisierten inhalten - je nach den defines halt.
    uU ist die xyz.hpp aber auch komplett generalisiert aufgebaut (ohne #ifdef) und in den jeweiligen .cpp Dateien befinden sich die #ifdefs

    1. Mit dieser Technik werden ja fast alle Methoden die speziell flag-abhängig sind in einer Impl datei landen. Angenommen ich habe parallele Kommunikation über OpenMP oder einen anderen Standard. Und ich will jetzt entweder Kommunikation zwischen prozessen oder eben nicht. Wie soll ich hier kapseln? Eine Methode komm() die eigentlich erst in den Impl* implementiert wird?

    Exakt.
    Du rufst komm() auf wenn du OpenMP willst (und definierst vorher eben USE_OPENMP) und du rufst komm() auf wenn du XYZ willst (und definierst vorher eben USE_XYZ) etc.

    Wenn du nun eine neue funktionalitaet hast, dann kannst du diese ohne aufwand in die anwendung integrieren. Weil um den super duper neuen multithreading weg zu waehlen, machst du einfach USE_SUPERDUPERTOLLERWEG und rufst komm() auf.

    Damit muss ich ja doch sämtlichen code in die Impl* dateien auslagern bzw. für jede unterschiedliche Zuweisung eine eigene Methode in der IMpl* datei...

    Du hast sicher nicht 100.000 einzelne Anweisungen. Du kannst sie zusammenfassen. In Funktionen. In Klassen. Irgendwie.

    Einfach nur einmal daran denken was passiert wenn ein neuer flag hinzukommt... Dann sollte alles von alleine klar sein.



  • Also wieder erstmal danke für die Hilfe. Und es tut mir echt leid - ich steh irgendwie auf dem schlauch...

    Ich weiß dass es etwas zu viel verlangt ist aber könnte mir jemand ein kurzes beispiel aufzeigen? Das würde mir glaube ich sehr helfen...

    Zusatzfrage: Ist diese Art von der Performance bremsend? Ich muss ja immer Methoden aufrufen...Oder wirkt sich das auf die Performance nicht merklich aus?


Anmelden zum Antworten