#ifdef -> einrücken oder nicht?



  • 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?



  • achso...jetzt klingelts langsam...
    ich versuche es nochmal mit eigenen worten in einem code-beispiel:

    //in der main.cpp z.B.
    
    // Also hier wird nur 1 variante includiert mit allen Methoden? Die Dateien
    // stehen unten implementiert.
    #ifdef MEIN_FLAG
    #   include "variante_1.hpp"
    #else
        inlude "variante_2.hpp"
    #endif
    
    int main()
    {
        int a = get_a();
    }
    
    //variante_1.hpp
    int get_a()
    {
       return 2;
    }
    
    //variante_2.hpp
    int get_a()
    {
       return 3;
    }
    

    Jetzt habe ich aber noch Fragen.

    1. Macht diese Methode auch Sinn wenn man von vornherein weiß dass man nur 1 Flag hat oder nicht. Also entweder das Flag wird gesetzt mit MEIN_FLAG oder eben nicht. ?

    2. Macht ihr/man sowas auch bei debug-flags? Also wenn man ein debug-code haben will? Wenn ja - dann müsste ja ohne debug-kompilierung in den verzweigten Methoden nix drin stehen oder?

    3. Ist es üblich diese verzweigungsdateien als *.hpp zu kennzeichnen? Oder generell nur als Header? Gibt es eine übliche syntax?

    4. Wie sieht es mit der Performance aus? Macht sich das bemerkbar?

    Danke euch 🙂


  • Administrator

    gruppa schrieb:

    1. Macht diese Methode auch Sinn wenn man von vornherein weiß dass man nur 1 Flag hat oder nicht. Also entweder das Flag wird gesetzt mit MEIN_FLAG oder eben nicht. ?

    Ich verstehe die Frage nicht wirklich. Meistens hat man nur 1 - 4 verschiedene Präprozessor Switches. WINDOWS, LINUX, MAC, DEBUG und teilweise vielleicht noch ein paar für Optionale Dinge. Zumindest beweg ich mich in dem Bereich.
    Oder hast du das irgendwie anders gemeint?

    gruppa schrieb:

    1. Macht ihr/man sowas auch bei debug-flags? Also wenn man ein debug-code haben will? Wenn ja - dann müsste ja ohne debug-kompilierung in den verzweigten Methoden nix drin stehen oder?

    Bei Debug-Code mache ich das meistens anders, da kapsle ich die Sache, wie bei einem Assert über ein Makro, da ich den Code schliesslich erst gar nicht drin haben möchte in der Release Version:

    #ifdef NDEBUG
    #  define MAKRO_DEBUG_OUT(x)
    #else
    #  define MAKRO_DEBUG_OUT(x) std::clog << (x) << std::enl
    #endif
    
    // Verwendung:
    MAKRO_DEBUG_OUT("Dies wird nur im Debug-Modus ausgegeben!");
    

    gruppa schrieb:

    1. Ist es üblich diese verzweigungsdateien als *.hpp zu kennzeichnen? Oder generell nur als Header? Gibt es eine übliche syntax?

    *.hpp verwenden gewisse Leute anstatt *.h, um die C Header von den C++ Header zu unterscheiden.
    Also in C *.h und *.c
    Und in C++ *.hpp und *.cpp

    Man verwendet allerdings nicht unbedingt nur Header. Man kann auch jeweils *.cpp Files dazunehmen und dann entsprechend die Objectfiles dazulinken. Ganz normal halt. Mir wäre da sonst kein spezieller Syntaxstandard bekannt.

    gruppa schrieb:

    1. Wie sieht es mit der Performance aus? Macht sich das bemerkbar?

    Denk darüber erst am Ende nach. Der Kompiler und Linker können vieles optimieren und nur ein Profiler kann dir sinnvoll sagen, wo die Engpässe sind. Premature optimisation is evil!

    Grüssli



  • Danke - ist denn das von mir gezeigte Beispiel nun richtig formuliert?Ist es so wie ihr/du es meintet?

    Ich verstehe die Frage nicht wirklich. Meistens hat man nur 1 - 4 verschiedene Präprozessor Switches. WINDOWS, LINUX, MAC, DEBUG und teilweise vielleicht noch ein paar für Optionale Dinge. Zumindest beweg ich mich in dem Bereich.
    Oder hast du das irgendwie anders gemeint?

    Naja - ich z.b. schreibe im Moment an etwas was sowohl seriell laufen soll als auch parallel. Im Falle dass es parallel kompiliert wird wird ein haufen an zusätzlichen/anderen Lib-Funktionen kompiliert wenn das flag gesetzt ist. In diesem Fall gibt es aber nur seriell oder eben parallel - also entweder das flag ist gesetzt oder nicht. Macht es da Sinn so zu kapseln - es wird nicht mehr dazukommen...

    Danke für die Idee mit dem Macro...zum debuggen...ersetzt er das makro dann einfach durch nix wenn nicht im debug-mode kompiliert wird?


  • Administrator

    gruppa schrieb:

    Danke - ist denn das von mir gezeigte Beispiel nun richtig formuliert?Ist es so wie ihr/du es meintet?

    Das Beispiel ist ein wenig klein, um eine sinnvolle Antwort zu geben. Ich habe es ja eigentlich schon so präsentiert, also ... 😉

    gruppa schrieb:

    Macht es da Sinn so zu kapseln - es wird nicht mehr dazukommen...

    Wieso sollte es keinen Sinn machen? Ich habe oft nur ein #ifdef , #else , #endif .
    Wie willst du es sonst machen?

    gruppa schrieb:

    Danke für die Idee mit dem Macro...zum debuggen...ersetzt er das makro dann einfach durch nix wenn nicht im debug-mode kompiliert wird?

    Korrekt. Verhält sich eben genau wie assert aus <cassert> . Der Code im Makro wird komplett gestrichen, bzw. eben durch nichts ersetzt, wenn das Makro NDEBUG gesetzt ist.

    Grüssli



  • Wieso sollte es keinen Sinn machen? Ich habe oft nur ein #ifdef, #else, #endif.
    Wie willst du es sonst machen?

    Ja ne ich mein ob es bei 1 Flag also einem if-zweig überhaupt Sinn macht? Also wenn man jetzt z.B. 2 oder 3 Flags hätte die jeweils ifdef->else->ifdef->else...hätten würde ich sicherlich verstehen - macht das bei 2 auch Sinn? Bitte nicht böse verstehen - ich frage mich ob das nicht mit Kanonen auf Spatzen schießen ist bei einem Programm das z.B. nur 5k-10k Zeilen hat oder so...

    Danke


Anmelden zum Antworten