#ifdef -> einrücken oder nicht?



  • 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


  • Administrator

    gruppa schrieb:

    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...

    Ich frag dich nochmals, wie willst du es sonst machen? Wie ich schon sagte, ich habe oft nur ein einziges "Flag", daran ist überhaupt nichts abnormal.
    Ob es für dein Programm sinn macht, dass kann ich nicht beurteilen, da ich das Programm nicht kenne. Aber eben, wie sehen denn die Alternativen aus, welche du hast?

    Grüssli



  • ja die alternativen sehen eben nur so aus dass man an jede stell wo flag-abhängig compiliert werden soll eben ein #ifdef->#else->#endif steht.

    Aber ok - ich habe jetzt den Hintergrund glaube ich verstanden...



  • ...gibt es hier im forum jemanden der das noch so macht? ich wüsste nur gern ob das üblich ist...



  • Wenn du meint, ob jemand #ifdef einrückt. Nein, ich mache es nicht. Bei include guards macht es gar keinen Sinn (sind sowieso nur ganz oben und ganz unten). Wenn abhängig von (PräPro-)Konstanten unterschiedlicher Code compiliert werden soll (also z.B. mitten in einer Funktion), dann macht es auch keinen Sinn - hier sollte (nur!) die normale Einrückung des zu kompilierenden Codes im Vordergrund stehen. Und bei kleineren Statements in Headern könne man es machen, ich halte es aber für unnötig, da diese bei mir eigentlich nie Dimensionen annehmen, die eine Einrückung nötig machen. Trotzdem finde ich aber Draveres Variante recht elegant (Raute am Anfang, eigentliche Anweisung eingerückt).



  • eh nein ihc meine nicht das einrücken sondern die variante alle #ifdefs in separater datei zu kapseln und dann nur einmal einzubinden...



  • ich z.b. schreibe im Moment an etwas was sowohl seriell laufen soll als auch parallel.

    Mache es einfach nur parallel oder nur sequentiell. Parallele Programme haben normalerweise eine ganz andere Architektur als sequentielle.



  • Nein das will ich nicht. Mir wurde eingetrichtert nach SingleSource Prinzip zu arbeiten und ich will das auch. Es wird ja wohl einen eleganten und effizienten und allgmein üblichen weg geben beides in einem code zu vereinen...



  • gruppa schrieb:

    Nein das will ich nicht. Mir wurde eingetrichtert nach SingleSource Prinzip zu arbeiten und ich will das auch. Es wird ja wohl einen eleganten und effizienten und allgmein üblichen weg geben beides in einem code zu vereinen...

    was knivil meint ist, dass sequentielle archtiekturen komplett anders aussehen als parallele. wenn du es richtig machen würdest, hättest du 2 komplett unterschiedliche designs. denn parallel löst du einfach probleme komplett anders...

    und beides mit dem selben design zu realisieren kann nicht optimal sein...



  • ok ....ich verstehe jetzt knivil aber bin überrascht...ich wüsste jetzt überhaupt nicht wo der unterschied sein sollte bzw. wieso man da unterschiede hat.
    Und wenn es unterschiede gibt, warum wird von Leuten die sich deutlich im Forschungsbereich etabliert haben verzählt, dass man sequentiell und parallel in einen code packen sollte...



  • Ich finde es sehr interessant, was hier geschrieben wird 🙂 Ich habe zu dem Thema auch eine Frage: Was ist zum Beispiel, wenn ich in C++ eine Socketklasse schreiben will, die Plattformunabhängig ist? Es handelt sich hierbei nur um ein Beispiel. Ich weiß, dass es sowas bereits gibt 🙂

    Sehe ich das richtig, dass ich die Klasse für jedes System neu schreiben soll?

    #ifdef WIN32
    #include "win32/Socket.h"
    //...
    #else
    #error Not supported yet.
    #endif
    

    Ich hätte ja sehr viel doppelten Code. Was mache ich dann zum Beispiel, wenn ein Bug gefunden wurde?
    Ich müsste in jeder Datei etwas abändern. Ist das nicht sehr fehleranfällig?

    In der Hinsicht habe ich meine Zweifel...

    MisterDoubt



  • *push



  • würde denn bezüglich der augangsfrage mit der bedingten kompilierung vielleicht funktions-pointer ein sinnvolle alternative ?



  • gruppa schrieb:

    ok ....ich verstehe jetzt knivil aber bin überrascht...ich wüsste jetzt überhaupt nicht wo der unterschied sein sollte bzw. wieso man da unterschiede hat.
    Und wenn es unterschiede gibt, warum wird von Leuten die sich deutlich im Forschungsbereich etabliert haben verzählt, dass man sequentiell und parallel in einen code packen sollte...

    Ne, das hast du falsch verstanden.

    Du kannst parallelen und sequentiellen Code schon gemeinsam haben (musst du sogar). Aber du löst Probleme unterschiedlich. Du kannst ein Problem zB parallel lösen oder aber sequentiell. Die Lösung wird bei beiden varianten funktionieren, aber komplett anders aussehen.

    Sehen wir uns zB IO an. Wir haben eine blockende Operation. Jetzt können wir diese zB über etwas select() ähnliches ansteuern um zu wissen wann wir welche Aktion setzen können, oder aber über signale die asynchron laufen.

    Beides funktioniert, aber der ganze ansatz ist ein komplett anderer.

    deshalb denke ich, dass es nicht sehr sinnvoll sein kann beides gleichzeitig anzubieten.



  • Ok danke - und nochmal zu meinem jetzigen vorhaben bezüglich der auslagerung der ifdefs.

    Ich habe jetzt vor für den sequentiellen code ein file anzulegen im ordner sequentiell/variante1.hpp und für den parallelen code ein file anzulegen im ordner parallel/variante2.hpp bzw. einfach nur die 2 files im source-verzeichnis.

    Und in jedes file abhängig von sequentiell/parallel die funktion mit gleichem namen von der syntax her zu benennen und über 1 ifdef dann in den jeweiligen *.cpp files einzubinden.

    Damit wird über den präprozessor immer nur ein file von den beiden eingebunden und die sequentielle oder parallele funktion angestoßen.

    Wäre das ein sauberer weg? War doch das was ihr (Dravere etc...) mir vorgeschlagen haben...oder?



  • Und dann fällt mir noch was ein bzw. eine Frage:

    Wenn ich jetzt z.B. in einem flag einiges ausführen möchte im anderen aber nichts... macht dann sowas sinn?

    //irgendwo in main.cpp z.B.
    
    finalize();
    
    //in file variante1.hpp
    
    void finalize()
    {
        delete ....//irgendwas
        lib_call_finalize();
    }
    
    //in file variante2.hpp
    
    void finalize()
    {
    
    }
    

Anmelden zum Antworten