ifdefs über virtuelle subklassen auslagern?
-
hmmm....das versteh ich net....bei dem beispiel von dir drakon wollte ich es so machen aber da geht so nicht:
class sequential { void do_this_stuff (int p /*...*/ ) { // mach was.. } }; class parallel { void do_this_stuff (int p /*...*/ ) { //Das ist der Library call MPI_Comm_Rank(); } }; template<typename technique> class manager { void general_stuff () { // mach allgemeines } void special () { technique t; t.do_this_stuff ( 2 ); } }; int main { #ifdef DParallel manager<parallel> m; #else manager<sequential> m; #endif m.general_stuff (); m.special (); }
Ja. Du willst ja, dass der Compiler den Code gar nicht erst sieht und das kannst du ja nur vor dem Kompilierungsprozess machen, also musst du da mit den defines arbeiten.
Jetzt bin ich verwirrt - ich dachte gerade die #ifdefs auszulagern wäre die idee gewesen - mir ist schon klar dass das so nicht geht aber irgendwie war mein Ziel ein anderes...nur ein einziges mal ein #ifdef sozusagen....
@der hüter der zeit: danke für deinen Vorschlag - ja ich verstehe so ungefähr was du meinst und denke so würde es gehen. Im endeffekt ist es halt aber wieder fast doppelter code um den man aber wohl nicht drumrum kommt danke schonmal!
-
Wenn für alle Instanzen immer das gleiche Verhalten benötigt wird, würde ich gar nicht erst Templates nehmen.
class manager { public: void general_stuff() { } void special { #ifdef DParallel parallel_special(); // die Aktionen bei parallel #else sequential_special(); // die Aktionen bei sequentiell #endif } };
Tut mir leid, wenn wir etwas aneinander vorbeireden... Evtl. versteh ich dein Problem auch falsch
-
hmm....ja ich bin am überlegen ob es denn noch alternativen gibt bezüglich des compilezeit polymorphismumses... danke für den weiteren vorschlag
du meintest dann wohl eher sowas oder?
class manager { public: void general_stuff() { } void special { #ifdef DParallel Parallel parallel; parallel.special1(); parallel.special2(); //... #else Sequential sequential; sequential.special1(); sequential.special2(); #endif } };
Obwohl - so ists auch ein schmarrrn.....herrgott....
-
Du kannst ja die anderen möglichen Implementierungen ausschalten:
// sollte immer gehen, also keine Auswahl class sequential { void do_this_stuff (int p /*...*/ ) { // mach was.. } }; // wird nur wenn definiert angeboten #ifdef DParallel class parallel { void do_this_stuff (int p /*...*/ ) { //Das ist der Library call MPI_Comm_Rank(); } }; #endif
Somit hast du ja das define ein einziges mal, nämlich dort, wo die Klasse definiert ist. Die anderen Implementierungen sind aber weiterhin möglich.
-
Wenn die Abläufe für beide Techniken (parallel und sequentiell) genau gleich aussehen, kannst du ja ein Funktionstemplate schreiben, um nicht Code duplizieren zu müssen:
template <class Technique> void special_action() { Technique t; t.special1(); t.special2(); } void manager::special() { #ifdef DParallel special_action<Parallel>(); #else special_action<Sequential>(); #endif }
Wenn du in special_action() auch auf private Member zugreifen musst, kannst du sie zu einer Memberfunktion machen.
-
@Der Hüter der Zeit:
Sein aktuelles Problem ist, denke ich, dass er Code hat, der auf anderen Plattformen gar nicht gehen kann, weil es die Bibliothek dazu gar nicht gibt. Also hat er Funktionen, die es gar nicht gibt und dann reicht es nicht nur die template Instanzierung einzugrenzen.EDIT:
Nein. Doch das geht, wenn die Funktion ein template ist.
-
Also wir sollten nochmal klar stellen, was du überhaupt willst.
Grundsätzlich hast du ja 2 Probleme. Du willst eine gewisse Art von Polymorphie, also Teile des Code einfach austauschen ohne wirklich etwas ändern zu müssen. Meiner Meinung nacht ist statische Polymorphie (templates) immer noch die besser Alternative, weil du genau das willst.Das zweite Problem ist, dass gewisse Teile des Codes auf gewissen Platformen gar nicht funktionieren können, weil die Bibliotheken gar nicht vorhanden sind oder die Funktionen anderst heissen.
Die Verwirrung, die hier entstanden ist, ist imo darauf zurückzu schliessen, dass die beiden Probleme gemischt wurden. Darum nochmal differenziert betrachtet. Du willst das hier:
int main () { //manager<parallel> m; manager<sequential> m; m.general_stuff (); m.special (); }
Also sozuagen entweder ist ist der manager parallel oder sequentiell. Dafür eignet sich ja, wie schon gesehen das arbeiten mit einer Policy.
Jetzt kommt das zweite Problem. Es können nicht alle parallel arbeiten, also soll, wenn die erste Zeile aktiv ist ein Compilerfehler kommen, wenn es nicht unterstützt wird, oder?Also können wir das parallel einfach mit einem ifdef umschliessen:
struct sequential { void do_this_stuff () {} }; #ifdef DParallel struct parallel { void do_this_stuff () { MPI_Comm_Rank(); } }; #endif
Alternativ können wir da eine template Funktion benutzen, weil diese erst Kompiliert wird, wenn sie instanziert wird.
struct sequential { template <class T> void do_this_stuff () {} }; struct parallel { template <class T> void do_this_stuff () { MPI_Comm_Rank(); } };
Das führt aber dazu, dass wir hier eine template Funktion haben, wo es eigentlich gar nicht nötig ist. (Sprich beim Funktionsaufuf muss ein template Parameter angegeben werden).
Mir sind noch andere Möglichkeiten eingefallen, aber die zu diskutieren bringt es erst, wenn die Anforderungen klarer sind.
Nun kannst du ja auch die Auswahl in der main mit einem define lösen, wenn du möchtest.
-
Danke euch beiden. Ich habe im Moment die template-variante mit dem umschließenden #ifdef. Somit also 2 #ifdefs - einmal in der main zum instantiieren und einmal das umschließende #ifdef des parallelen anteiles. so kompiliert es jetzt. ich arbeite weiter und melde mich bei meiner nächsten frage danke euch !
-
Ok - also die template-variante und auch die idee mit einer abstrakten basisklasse für die beiden typen double und complex geht jetzt. Mein letztes Problem ist nun beides unter einen Hut zu bringen.
Dabei besteht mein Hauptproblem darin dass ich innerhalb der subklassen (double, complex) ebenfalls in den implementierten methoden auf die methoden aus sequential oder parallel zugreifen muss/will. also auch dort wieder auf ein objekt von manager zugreifen will sozusagen.
Ich wieß aber nicht wie ich das bewerkstelligen soll. ich kann ja nicht ein objekt manager an Base (von double und complex) übergeben weil damit müsste ja base templatebasiert sein. andererseits macht es wenig sinn innerhalb von manager die sachen anzulegen oder wäre das der richtige weg?
Oder anders gefragt:
Wie kann denn eine Methode einer Klasse (hier Complex z.B) die Subklasse von Base ist auf eine templatebasierte Methode einer anderen Klasse zugreifen?
-
Grundsätzlich hast du ja 2 Probleme. Du willst eine gewisse Art von Polymorphie, also Teile des Code einfach austauschen ohne wirklich etwas ändern zu müssen. Meiner Meinung nacht ist statische Polymorphie (templates) immer noch die besser Alternative, weil du genau das willst.
Warum hat man nicht 2 verschiedene Kompilierungseinheiten, die man entsprechend seinen Wuenschen zum gegeben Projekt dazu linkt. Gesteuert wird es halt ueber ein entsprechendes build script / system. Dann hat man weder stoerende ifdef's noch irgendwelche Templates. Der Code ist sauberer.
-
Warum hat man nicht 2 verschiedene Kompilierungseinheiten, die man entsprechend seinen Wuenschen zum gegeben Projekt dazu linkt. Gesteuert wird es halt ueber ein entsprechendes build script / system. Dann hat man weder stoerende ifdef's noch irgendwelche Templates. Der Code ist sauberer.
wie meinst dudas? hast du ein kleines minimalbeispiel?
-
Headerfile "do_something.h" der Funktion:
int do_something();
Implementationsdatei "do_something_parallel.cpp":
int do_something() { //do it parallel }
Implementationsdatei "do_something_sequential.cpp":
int do_something() { //do it sequential }
#include "do_something_parrallel.h" int main() { do_something(); }
So, nu wird abhaengig davon, was man will, entweder die parallele Variante kompiliert oder die sequentielle. Ein Beispiel ohne makefile mit gcc fuer die sequentielle Variante:
g++ do_something_sequential.cpp main.cpp
-
#include "do_something_parrallel.h" int main() { do_something(); }
fehlt da nicht was? so inkludierst du ja nur die parallele version. Oder muss man dann immer die main methode anpassen bevor kompiliert wird?
-
Ich denke, der include-Name ist nur unglücklich gewählt^^
Richtiger wäre vrmtl
#include "do_something.h"
dort muss ja an und für sich nur
void do_something();
drin stehen - die fkt wird dann einfach je nach der ausgewählten cpp-datei definiert...bb
-
aeh, ja, das meine ich.