#ifdef -> einrücken oder nicht?



  • Ich will euch nicht ueberzeugen. Auch koennt ihr euch ruhig lustig machen. Es sind Tools, die einem Arbeit abnehmen. Wenn keine verschiedenen Implementationen, keine Prebuild-/Postbuildsteps noetig sind oder z.B. kein Content aus Daten generiert wird ... dann braucht man ja auch kein Tool, das das automatisiert. Wenn ihr wirklich wissen wollt, wie es funktioniert, dann sind die Manuals besser als ein Forenpost.



  • mich würde es einfach interessieren ob es eine aktzeptable alternative zu den #includes ist.

    mir fällt keine möglichkeit ein es dynamisch genug zu bauen ohne komplexe buildscripte zu verwenden. während die #include variante halt 'deppensicher' ist.

    und vorallem ist sie konsistent mit der variante bei header files - da man es dort ja auch so macht.

    aber uU bieten buildscripte hier einen gewissen vorteil - nur dazu musst du etwas deutlicher werden. denn ich sehe beim besten willen nicht wie ich das ohne komplexe scripte implementieren soll.

    wo die inklude variante zB probleme hat ist wenn du mehrere .cpp dateien für die implementierung einer proxy .cpp datei brauchst. kommt selten vor und ist eher ein design fehler dann, aber es kommt vor und da muss man eben per #include die notwendigen .cpp dateien wieder inkludieren und das ist einfach doof.

    deshalb bin ich immer auf der suche nach besseren möglichkeiten...

    PS:
    die #include variante nimmt einem nicht das schreiben von build scripten ab. die braucht man sowieso. es geht also nicht pro/contra build script, sondern welche lösungen für das problem (mit den mehreren sets an flags) gibt es.



  • Ok...@Shade - 3 kurze weitere fragen - langsam überzeugt mich dein Vorgehen 🙂

    1. Wenn im einen Zweig inline eine funktion ist, muss sie es doch im anderen zweig nicht sein oder?

    2. wie machst du es wenn eine funktion z.B. foo() in einem zweig ist und im anderen foo(int a) ? beim aufruf im hauptcode muss dann doch ein ifdef stehen oder nicht? ich versteh deinen ansatz mit den proxy-files nur wenn beide funktionen die gleiche signatur haben.

    3. für funktionen die etwas rechnen aber in z.B. X/xyz.hpp deklariert sind. Wie kann ich diese in ein X/xyz.cpp file schreiben? ALso wie gehst du mit definitionen um die im X/ Ordern liegen sollen?

    Danke



  • mich würde es einfach interessieren ob es eine aktzeptable alternative zu den #includes ist.

    Wenn 3 Zeilen einfacher sind als 10, dann nicht. Gegenfrage: Wann werden #ifdefs kompliziert?

    nur dazu musst du etwas deutlicher werden.

    Das wuerde leider zu umfangreich werden. Noetig wird es erst, wenn du z.B. Software fuer die Allgemeinheit schreibst. Unter Linux ./configure, make, make install. Unter Windows hat man ein Setupprogramm. Dort kompiliert man aber auch selten was als Benutzer. configure testet auf Header und benoetigte Bibliotheken und erzeugt dir dann dein makefile gemaess den angegebenen Optionen (z.B. mit Threads, ob pthreads oder was auch immer, mit gnome oder ohne ... aha SSE2 also diese Datei uebersetzen und nicht die fuer Cell optimiert und auch sonst die entsprechenden Compileroptionen setzen ...). Um dieses Skript zu erzeugen, gibt es wiederum (auch graphische) Tools.

    enn ich sehe beim besten willen nicht wie ich das ohne komplexe scripte implementieren soll.

    Einfache Probleme sollten einfache Loesungen haben. Meine makefile-Variante ist aber auch nicht komplizierter oder gar komplex. Mein Standard-Makefile hat ganze 20 Zeilen (ohne Leerzeilen). Ich loese das Problem halt ueber den Linker, du ueber den Kompiler. Und fuer komplexe Buildskripte gibt es auch Tools, die einem viel Arbeit abnehmen.



  • gruppa schrieb:

    1. Wenn im einen Zweig inline eine funktion ist, muss sie es doch im anderen zweig nicht sein oder?

    Exakt. Wenn du zB eine Funktion lock() hast, die in einem nicht multi-threading programm nichts tut, dann packst du sie einfach in den header

    void lock(Foo whatever) {
    }
    

    und der compiler optimiert sie komplett raus.
    Wenn sie dann aber etwas in einem multi-threaded programm tut, dann packst du nur noch die deklaration in die hpp Datei und die implementierung in die passende cpp Datei.

    1. wie machst du es wenn eine funktion z.B. foo() in einem zweig ist und im anderen foo(int a) ? beim aufruf im hauptcode muss dann doch ein ifdef stehen oder nicht? ich versteh deinen ansatz mit den proxy-files nur wenn beide funktionen die gleiche signatur haben.

    Die funktion muss die gleiche signatur haben.
    Wenn wir zB von locks ausgehen, wo du in einem parallelen programm eben locks halten musst die du in einem seriellen logischerweise nicht brauchst, wuerde mein code dennoch so aussehen:

    void foo() {
      lock l(bar);
    
      do_something(l);
    }
    

    Nur dass lock in einem seriellen programm halt nichts tut, also alle funktionen haben einen leeren koerper. oft ist es dann auch sinnvoll statt
    lock.unlock() ein "unlock(lock)" zu schreiben, und unlock ruft dann entweder lock.unlock auf oder tut garnichts.

    1. für funktionen die etwas rechnen aber in z.B. X/xyz.hpp deklariert sind. Wie kann ich diese in ein X/xyz.cpp file schreiben? ALso wie gehst du mit definitionen um die im X/ Ordern liegen sollen?

    Diese Frage verstehe ich nicht. Erklaer bitte genauer was du meinst.

    Danke[/quote]

    knivil schrieb:

    Wenn 3 Zeilen einfacher sind als 10, dann nicht. Gegenfrage: Wann werden #ifdefs kompliziert?

    Eigentlich zu keinem Zeitpunkt. Sie sind nur dann unpraktisch, wenn man mehrere helper klassen braucht und diese dann in das .cpp File includen muss anstatt sie als eigene uebersetzungseinheit zu verwenden.

    Das wuerde leider zu umfangreich werden. Noetig wird es erst, wenn du z.B. Software fuer die Allgemeinheit schreibst. Unter Linux ./configure, make, make install. Unter Windows hat man ein Setupprogramm. Dort kompiliert man aber auch selten was als Benutzer. configure testet auf Header und benoetigte Bibliotheken und erzeugt dir dann dein makefile gemaess den angegebenen Optionen (z.B. mit Threads, ob pthreads oder was auch immer, mit gnome oder ohne ... aha SSE2 also diese Datei uebersetzen und nicht die fuer Cell optimiert und auch sonst die entsprechenden Compileroptionen setzen ...). Um dieses Skript zu erzeugen, gibt es wiederum (auch graphische) Tools.

    danke, ich kenne die gaengigen tools. wie gesagt: build scripte braucht man so oder so. da fuehrt kein weg vorbei.

    nur wie implementierst du eine loesung fuer das hier vorliegende problem ohne zu komplex zu werden. das ist die frage.

    enn ich sehe beim besten willen nicht wie ich das ohne komplexe scripte implementieren soll.

    Einfache Probleme sollten einfache Loesungen haben. Meine makefile-Variante ist aber auch nicht komplizierter oder gar komplex. Mein Standard-Makefile hat ganze 20 Zeilen (ohne Leerzeilen). Ich loese das Problem halt ueber den Linker, du ueber den Kompiler. Und fuer komplexe Buildskripte gibt es auch Tools, die einem viel Arbeit abnehmen.

    die frage ist aber: wie loest du es.
    und was der linker damit zu tun hat verstehe ich auch nicht.
    oder kompilierst du immer alles und linkst dann nur die relevanten sachen? das kanns ja nicht sein, oder?



  • Ich hatte mal ne Crossplattform-Bibliothek geschrieben, da fande ich die Makefile-Lösung wesentlich einfacher. Es waren nur Implementierungen, also die .cpp-Dateien, welche konfigurationsabhängig waren, damit sah mein Verzeichnisbaum etwa so aus:

    include
    source
    source/win
    source/linux
    

    Unter Windows im VS hatte ich einfach die nur die normalen- und Windows-Sourcen im Arbeitsbereich, bei Linux sah das Makefile etwa so aus:

    g++ source/* source/linux/*
    

    Das zu Warten war wirklich einfach: Wenn 'ne neue Source-Datei dazukommt, einfach die entsprechende im Windows- und Linux-Verzeichnis ablegen und im VS die Win-Version in den Arbeitsbereich aufnehmen. Das wars.
    Bei der #ifdef-Variante bräuchtest du für jede Source-Datei eine zusätzliche Proxy-Datei. Allerdings war mein Fall auch ein Spezialfall, da es nur auf einen einzigen Compilerswitch ankam und nur Implementationen variabel waren.



  • @Badestrand:
    jetzt bau in dein system mal bitte eine variante mit Qt und eine mit gtk ein.

    und wenn wir lustig sind noch jeweils einmal mit boost::thread und einmal mit nativen threads.

    Du kannst natuerlich jetzt
    src/win
    src/qt
    src/gtk
    src/lin
    src/lin_threads
    src/win_threads
    src/boost_threads

    machen. nur wie kompilierst du dann? klar, kann man ueber das build script sagen was man will, aber wie komplex wird das abfragen und das setzen der jeweiligen defines? das ist ja das interessante.
    ein
    build USE_NATIVE_THREADS USE_LINUX USE_GTK
    waere echt super zum builden. geht das mit vernuenftigen aufwand?

    das problem ist naemlich ploetzlich: was sind native threads? je nach plattform wieder etwas anderes...

    wuerde mich echt interessieren wie das vernuenftig geht



  • Shade Of Mine schrieb:

    jetzt bau in dein system mal bitte eine variante mit Qt und eine mit gtk ein.

    Bloß nicht 😃 Ne, ich kenne mich mit Makefiles und Buildsystemen kaum aus und ich schrieb ja auch, dass meine Variante ein "Spezialfall" war.

    Nur mal in's Blaue: Für die Auswahl
    src/lin_threads
    src/win_threads
    src/boost_threads
    könnte man doch ein kleines Bash-Skript schreiben, welches für jeden Parameter den enstprechenden Ordner kompiliert. Oder so. Oder nicht?



  • ich vermute, daß das, was ich brauche, einfach nur ein makefile ist, das an sich nur vor hat, die main.cpp zu compilieren. und außerdem bei jedem inkludierten header foo.hpp falls eine foo.cpp existiert, die auch noch. und halt alle, die davon inkludiert werden...



  • Danke erneut.

    Ich hab leider knivils Ansatz immer noch nicht verstanden. Er zeigte zwar das Makefile aber die Ordnerstruktur bzw. ein kleines Beispiel nicht...sind da die #ifdefs einfach zwischen dem code ? Oder ist der code meist doppelt vorhanden ? Evtl. war das bei badestrand so bei der cross-plattform-lösung?

    ich vermute, daß das, was ich brauche, einfach nur ein makefile ist, das an sich nur vor hat, die main.cpp zu compilieren. und außerdem bei jedem inkludierten header foo.hpp falls eine foo.cpp existiert, die auch noch. und halt alle, die davon inkludiert werden...

    😕

    bitte was? Bzw. in welchem zusammenhang?

    Und noch eine Frage an Shade:
    so wie ich das verstanden habe hast Du ja jetzt nur ein einziges #ifdef welches in einer separaten datei liegt und welches die flag-abhängige datei aus dem Ordern X/xyz.hpp includiert. Somit musst Du ja in jedem File/Klasse die Zugriff auf eine Funktion aus xyz.hpp hat das komplette file includieren. Damit ist doch dann immer das komplette file includiert und nicht nur die spezielle funktionsdeklaration die gerade in der klasse z.B. verwendet werden soll. Habe ich das richtig verstanden?



  • *push



  • Nochmal an Shade:

    Das heißt du hast in einem Ordner variante1 ein file namens variante1.hpp liegen und in einem ordner variante2 ein file variante2.hpp liegen.
    Dort wo deine hauptsourcen also die ifdef-freien sourcen liegen liegt auch ein verzweigungsfile xyz.hpp welches das eine ifdef hat und entweder variante1/variante1.hpp includiert oder eben variante2/variante2.hpp.

    Somit stehen in variante1.hpp ALLE Fuktionen die du durch bedingte kompilierung verwendest. Umgekehrt auch in variante2.

    Frage: Jetzt wird doch aber immer eines der beiden files komplett eingebunden. Nehmen wir an variante1.hpp hat 10 verschiedene funktionsdefinittionen und du brauchst in der main.cpp nur eine davon. Dann werden ja trotzdem alle 10 oben eingebunden oder?
    Sehe ich das richtig? So wird in jedem file das auf die eine variante zugreift das komplette variante1-file includiert. ist doch bissal zu viel des guten doer nicht?



  • die trennung mit #ifdef/#include nach verwendeten implemetierungen und die trennung nach zweck durch verschiedene dateinamen sind völlig unabhängig voneinander.



  • die trennung mit #ifdef/#include nach verwendeten implemetierungen und die trennung nach zweck durch verschiedene dateinamen sind völlig unabhängig voneinander.

    sorry ich versteh dich net volkard, kannst des nochmal anders sagen? danke



  • Er meint, dass du zwei unabhängige Probleme vermischst:

    1. Bedingte Kompilierung, also je nach Konfiguration soll anderer Code kompiliert werden
    2. Ob eine Header-Datei evtl in mehrere aufgesplittet werden sollte

    Oder andersrum:

    Jetzt wird doch aber immer eines der beiden files komplett eingebunden. Nehmen wir an variante1.hpp hat 10 verschiedene funktionsdefinittionen und du brauchst in der main.cpp nur eine davon.
    Die Situation hast du bei fast jeder Header-Datei und hat nüscht mit der bedingten Kompilierung (#ifdef/..) zu tun.



  • hmm ok...

    auf diese art und weise muss ich aber wenn ich z.B. nur eine einzelne variable holen will die z.B. in der parallelen umgebung über eine spezielle lib-funktion realisiert wird schon eine ganze funktion schreiben.

    D.H. also ich muss für auch nur eine anweisung eine funktion schreiben. Wenn die funktion jetzt im programm vielleicht 10 mal gebraucht wird macht es doch nicht sinn die als inline zu setzen oder? Oder "darf" bzw. sollte ich das in dem fall sogar?

    Wenn ich jetzt für 100e anweisungen die ich NICHT zusammenfassen kann funktionen habe, bremst das ja etwas... versteh ich das richtig?



  • und dann versteh ich nicht warums mir gerade alles um die ohren haut.

    folgendes in pseudocode:

    //xyz.hpp
    
    #ifdef MYFLAG
    #    include xyz1/xyz.hpp
    #else
    #    include xyz2/xyz.hpp
    #endif
    
    #include "xyz.hpp"
    #include "classA.hpp"
    
    int main()
    {
    
    }
    
    //classA.hpp
    
    #include "xyz.hpp"
    

    er sagt multiple definitions of <einer funktion>. Also irgendwie funktioniert die header-einbindung nicht. warum denn? ich habe die include-guards überall drin. mein xyz.hpp sieht z.B. so aus:

    #ifndef IMPL_xyz_H
    #define IMPL_xyz_H
    
    void
    Init(int& n, int& m)
    {
        m = 1;
        n = 0;
    }
    
    #endif
    


  • ich bräuchte hier nochmal Hilfe. Ich dachte Deklaration und Definition sollten im Header stehen. So bekomme ich aber Konflikte mit multiple definitions...



  • gruppa schrieb:

    ich bräuchte hier nochmal Hilfe. Ich dachte Deklaration und Definition sollten im Header stehen. So bekomme ich aber Konflikte mit multiple definitions...

    Ich kenn jetzt nicht den ganzen Thread, aber das hört sich nach Linker-Fehler an. Normalerweise packt man die Deklaration in den Header, die Definition/Implementation in das cpp-File...



  • Ja ich verstehe das Problem und auch Deine Antwort. Ich frage mich nur wie "Shade Of Mine" es jetzt gemeint hat weil er irgendwo geschrieben hat dass er alles in einen header packt...oder meinte er nur die deklaration?


Anmelden zum Antworten