#ifdef -> einrücken oder nicht?



  • 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()
    {
    
    }
    


  • ja und ja.



  • Danke erneut für die Antwort. Könnte man das auch irgendwie über Makros realisieren. Also ein Makro setzen in der main z.B. und dann die verzweigung in separatem file oder so ähnlich? Ginge das und wenn ja - wie?



  • gruppa schrieb:

    Danke erneut für die Antwort. Könnte man das auch irgendwie über Makros realisieren. Also ein Makro setzen in der main z.B. und dann die verzweigung in separatem file oder so ähnlich? Ginge das und wenn ja - wie?

    Dafür hast du ja die proxy dateien.
    foo.cpp sieht zB so aus:

    #ifdef X
    #  include "X/foo.cpp"
    #elif Y
    #  include "Y/foo.cpp"
    ...
    


  • bitte nicht wundern warum ich so nachbohre...es interessiert mich einfach:

    zu schadeofmine: hmm...meinst Du jetzt dass das so gesehen das gleiche ist? Über Makros und über die auslagerung der ifdefs?

    Ich würde gerne noch folgendes wissen und hoffe auf die Geduld der Leser 🙂

    * Wenn ich jetzt über eine bedingte Kompilierung eine leere Methode einbinde (wie z.B. das finalize() vom vorigen post) - ist das von der Performance her bremsed. Natürlich nur unwesentlich aber wenn es z.B. sehr oft vorkommen würde...? Nur aus Interesse die Frage...

    * Würde man vielleicht sowas auch über function-pointer lösen - also über ein if-else einen function-pointer zur laufzeit definieren und dann über diesen in die richtige methode switchen? Oder wäre das von der performance her langsamer oder gar unübersichtlicher?



  • gruppa schrieb:

    * Wenn ich jetzt über eine bedingte Kompilierung eine leere Methode einbinde (wie z.B. das finalize() vom vorigen post) - ist das von der Performance her bremsed. Natürlich nur unwesentlich aber wenn es z.B. sehr oft vorkommen würde...? Nur aus Interesse die Frage...

    ja, bremst, wenn finalize() nicht inline ist und in einer anderen cpp-datei wohnt und der compiler sie nicht einfach wegoptimieren kann.
    wenn sie in einem header wohnt und inline ist, kostet das gar nix.



  • und wenn ich über -O3 kompiliere - werden da nicht alle funktionen als inline behandelt? Oder red ich jetzt völligen quatsch?



  • gruppa schrieb:

    und wenn ich über -O3 kompiliere - werden da nicht alle funktionen als inline behandelt? Oder red ich jetzt völligen quatsch?

    inline bedeutet nicht das der code schneller wird, er kann auch langsamer werden...

    was volkard aber gemeint hat ist, wenn der compiler die funktion inlinen kann und sieht dass die funktion leer ist und spart es sich code zu generieren.

    je nachdem wie clever ein compiler ist, desto mehr funktionen könnte er inlinen. der vc++ zB kann funktionen über mehrere .cpp Dateien hinweg inlinen - was aber jeder kann ist wenn die funktion in einem header steht.



  • ah ok- danke für die Antwort.

    jetzt mal wieder ne blöde frage: eine funktion im header - damit ist gemeint sie soll einfach roh in einem header-file (z.B. variante1.hpp) liegen?

    Wo kann eine Funktion noch liegen wenn nicht im header? Als methode einer Klasse?



  • gruppa schrieb:

    Wo kann eine Funktion noch liegen wenn nicht im header? Als methode einer Klasse?

    Die funktions definition muss im header liegen damit die compiler problemlos geinlined werden können. die deklaration muss sowieso im header stehen.

    definition:
    void foo() {}

    deklaration:
    void foo();



  • Shade Of Mine schrieb:

    Dafür hast du ja die proxy dateien.
    foo.cpp sieht zB so aus:

    #ifdef X
    #  include "X/foo.cpp"
    #elif Y
    #  include "Y/foo.cpp"
    ...
    

    Sowas wuerde ich nie machen, da ich es als viel sauberer empfinde, es ueber entsprechende Build-/Linkeinstellungen (Skripte, what ever) zu steuern. Ich finde es einfach haesslich.



  • knivil schrieb:

    Sowas wuerde ich nie machen, da ich es als viel sauberer empfinde, es ueber entsprechende Build-/Linkeinstellungen (Skripte, what ever) zu steuern. Ich finde es einfach haesslich.

    dh du musst dauernd die build scripte anpassen...?

    das wäre zB für mich ein no-go.
    der vorteil von meiner #include variante ist, das alles transparent abläuft. du kompilierst die foo.cpp und die flags definieren was genau passiert.

    denn wozu muss man wissen dass foo.cpp in wirklichkeit 3 unterschiedliche implementierungen hat? und vorallem wann diese verwendet werden. diese logik würde ich zB nie in ein buildscript packen. da sie sich nämlich konstant ändern kann. und build scripte sind in meinen augen etwas konstantes woran man nicht unnötig viel herum manipulieren sollte...



  • dh du musst dauernd die build scripte anpassen...?

    Sagen wir mal "erweitern" und sollte nicht schwerer sein als ein weiteres #ifdef in deiner cpp-Datei hinzuzufuegen (kommt auf die Flexibilitaet deines Buildsystems an, k.A. was Visual Studio da so anbietet). Die Buildskripte musst du bei deiner Variante auch anfassen, denn wo soll X, Y etc definieren werden, wenn nicht als Compileroption.

    wissen dass foo.cpp in wirklichkeit 3 unterschiedliche implementierungen hat?

    Ja, musst du auch sonst, steht ja in deiner cpp-Datei. Musst du aber auch wieder nicht, da ja das Buildskript alles macht. Wenn foo 3 unterschiedliche Implementierungen fuer 3 unterschiedliche Plattformen (oder Features) hat, dann steht in den Buildskripten das drin, welche uebersetzt und gelinkt werden soll. Und ob es nun in einer cpp-Datei drin steht oder im Buildskript ist erstmal vom Aufwand egal. Ist es aber ausserhalb definiert, besteht eine klare Trennung zwischen Code und Build-/Linkoptionen. Bestehenden Code moechte ich nicht anfassen, wenn ich z.B. eine Plattform unterstuetzen moechte (zumindestens in einer idealen Welt).



  • @knivil: kannst du ein minimales beispiel bringen wie du das machen würdest - interessiert mich... wäre nett danke



  • Dieses minimale Beispiel zeigt ein makefile, wie es aussehen koennte (prinzipiell). Mittels "make ogl" wuerde man die Version bauen, deren Rendererimplementation OpenGl benutzt, mittels "make directx" halt fuer DirectX. Das dargestellte ist sehr vereinfacht. Auch sieht man hier 2 Zeilen, die fast gleich aussehen -> "Codeduplikate" -> eher schlecht. Das kann alles umgangen werden, mit zusaetzlichen Variablen etc.

    Auch ist make ein eher krudes Tool, so dass damit Arbeiten nicht wirklich Spass macht. Andere Alternativen sind autoconf (das makefiles fuer die ensprechende Plattform/Features erzeugt) oder Apache Ant. Es gibt viele verschiedene Tools. Ein Vorteil ist z.B. dass man mit einem Aufruf auch Dokumentation, Bilddaten ... erzeugen lassen, automatische Tests starten kann etc. Fuer den Heimeinsatz ist das aber ueberdimensioniert, auch sollte man sich ueberlegen, ob es einem Arbeit abnimmt. Deswegen bleibe ich bei make, obwohl es eher hoelzern und kryptisch ist. Z.B. nutze ich make insbesondere beim Schreiben von Texten mit Latex.

    OBJECTS = ... many .o files
    
    ogl: $(OBJECTS) RendererOpenGl.o
      g++ $(OBJECTS) RendererOpenGl.o -o app_ogl
    
    directx: $(OBJECTS) RendererDirectX.o
      g++ $(OBJECTS) RendererDirectX.o -o app_direct
    


  • danke kniviel aber leider versteh ichs noch nicht.

    So muss man doch komplett doppelten code schreiben oder nicht? Ich muss doch sowohl für opengl also auch directx dann eine funktionsfähige codebasis haben?

    kannst du das auf unser minimalbeispiel übertragen?



  • da ich meine makefiles nicht dauernd pflegen will, werde ich Shades vorschläge übernehmen.


Anmelden zum Antworten