Programm kompatibel halten mit defines
-
Hi!
Ich habe eigentlich zwei Fragen:
1.) Mein Programm ist soweit es geht kompatibel geschrieben, d.h. es lässt sich mit wenig Änderungen auch auf Linuxsystem compilieren.
Jetzt wollte ich das so machen das ich an den entsprechenden Stellen einfach schreibe#ifdef LINUX //Linux #else // Windows #endif
Gibt es dagegen Einwände? Könnte ich das noch besser lösen?
2.)
Ich müsste, da ich mein Programm erweiter, Ordner erstellen, da dass ja spezifisch ist wollte ich fragen wie man unter Unix Ordner erstellen kann, unter Windows würde ich... system verwenden
-
Definer schrieb:
Gibt es dagegen Einwände?
Ja. Solcher Code ist schwer zu lesen, schwer zu warten und schwerer zu testen. Zumindest auf Funktionsebene solltest du das besser sein lassen. Auf globaler Ebene hingegen ist es in Maßen ok.
Könnte ich das noch besser lösen?
Ja. Z.B.
// func.h void func(); // win32/func.cpp #include "../func.h" void func() { // win32 abhängiger Code } // linux/func.cpp #include "../func.h" void func() { // }
Die Entscheidung welche cpp-Datei gebaut wird, triffst du dann in deinem Makefile.
Dazu kannst du dir auch einen Header bauen, in dem die Platform bestimmt und dann entsprechende Header inkludiert werden.
Vorteil: Du hast die #ifdefs nur an einer zentralen Stelle und der rest deines Codes bleibt davon verschont.
-
1.) keine von meiner seite, mach ich genau so
2.) mkdir wäre der befehl. ich denke mit system könnte es gehen, aber wissen tu ich es nicht.
-
-
Da schließ ich mich Hume an. Aber
-
am besten nimmst du dafür einfach Platformunabhängige Librarys, wie boost::filesystem. Dann ist dein Programm auch noch portabler. Ansonsten man: mkdir(2)
-
-
Hmm, also es sind wirklich Kleinigkeiten und ich hantiere eigentlich so gut wie nie mit makefiles rum, die Lösung mit den 2 cpp Files wäre für mich overpowered, da es wirklich minimale Änderungen sind und ich somit 2 zu 98% identische cpp's hätte.
**
Zumindest auf Funktionsebene solltest du das besser sein lassen. Auf globaler Ebene hingegen ist es in Maßen ok.**Was bedeutet Funktionsebene? Heißt es, ich soll innerhalb von Funktionen hierauf:
#ifdef LINUX //Linux #else // Windows #endif
verzichten, aber bei z.B. globalen Variablen wäre das eher i.O.? Ich persönlich fand #define für Kleinigkeiten gar nicht so schlecht, wobei ich mir das ehrlich gesagt ein wenig abgeschaut habe (hab schon öfters gelesen "#ifdef WIN32")
-
HumeSikkins schrieb:
Ja. Solcher Code ist schwer zu lesen, schwer zu warten und schwerer zu testen. Zumindest auf Funktionsebene solltest du das besser sein lassen. Auf globaler Ebene hingegen ist es in Maßen ok.
findest du sowas schlecht und unleserlich ?
// zentraler header #if defined(_WIN32) #define PLUGIN_HANDLE HINSTANCE #define PLUGIN_LOAD(a) LoadLibrary(a) #define PLUGIN_GETADR(a, b) GetProcAddress(a, b) #define PLUGIN_UNLOAD(a) FreeLibrary(a) // ... #elif defined(LINUX) #define PLUGIN_HANDLE void *; #define PLUGIN_LOAD(a) dlopen(a, RTLD_LAZY) #define PLUGIN_GETADR(a, b) dlsym(a, b) #define PLUGIN_UNLOAD(a) dlclose(a) // ... #endif // verwendung PLUGIN_HANDLE m_Handle = (PLUGIN_HANDLE)PLUGIN_LOAD(szName); // usw usw
ich finde das ganz gut. ist das nicht ehr eine stil frage ? oder spricht irgendwas gravierendes dagegen ?
-
oder spricht irgendwas gravierendes dagegen ?
die existenz von konstanten,enums, typedefs und templates?
-
Na ja, erstens sollte man #defines für so etwas ja vermeiden und zweitens wer benennt die dann "WIN32" oder "LINUX"?
-
otze schrieb:
die existenz von konstanten,enums, typedefs und templates?
klar könnte ich diesen auch so lösen aber es ging mir mal um richtig oder krautfalsch oder stilfrage. denn ich sehe keinen fehler dieses so zu machen wegen vier funktionen. wenn es allerdings aufwendiger wird (erstellung eines fenster's als beispiel) dann halte ich mich natürlich an deine variante.
Definer schrieb:
Na ja, erstens sollte man #defines für so etwas ja vermeiden und
aha, sehr gute begründung. sollte in die faq.
Definer schrieb:
zweitens wer benennt die dann "WIN32" oder "LINUX"?
windows kompilier zum bespiel siehe http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_predir_predefined_macros.asp
-
wieso man keine defines benutzt:
1. ein define macht kein halt vor scopegrenzen
2. da es sich nicht an scopegrenzen hält, kann es dir im flal eines schlechten namenssystems ganz schnell ein paar hard to find bugs bescheeren, bei const/typedef/enum etc ist dieses problem nicht so extrem.3. zu deinem problem würden traits wunderbar passen
-
@miller_m
Furchtbar! Schrecklichstes C Mittelalter!Für so etwas schreibt man sich einen schönen Wrapper und macht das dann so wie Hume es gezeigt hat. Ansonsten kann man Platformabhängige Sachen auch leicht mit Policies kapseln und dann folgendes machen
//posix/policy.hpp struct posix_policy { //... POSIX kompatibler Code }; //win32/policy.hpp struct win32_policy { //... WIN32 kompatibler Code }; //policy.hpp #ifdef POSIX #include "posix/policy.hpp" typedef posix_policy policy; #elif WIN32 #include "win32/policy.hpp" typedef win32_policy policy; #else #error "Keine Policy für die Platform vorhanden" #endif //klasse.hpp #include "policy.hpp" template<class Policy> class klasse_policy { //... }; typedef klasse_policy<policy> klasse;
-
king, würde in dem fall der ausdruck traits nicht weitaus treffender sein, als policy?
oder verwechsel ich da wiedermal was?
-
[quote="miller_m"]
otze schrieb:
Definer schrieb:
Na ja, erstens sollte man #defines für so etwas ja vermeiden und
aha, sehr gute begründung. sollte in die faq.
Otze hat es zwar schon beantwortet aber ansonsten kannst du noch effective C++ lesen, wobei ich eher glaube das du weißt "warum"
-
otze schrieb:
king, würde in dem fall der ausdruck traits nicht weitaus treffender sein, als policy?
oder verwechsel ich da wiedermal was?
Nein, Policy ist schon richtig. Traits beschreiben ja eher eine Policy für einen Typ
-
miller_m schrieb:
findest du sowas schlecht und unleserlich ?
// zentraler header #if defined(_WIN32) #define PLUGIN_HANDLE HINSTANCE #define PLUGIN_LOAD(a) LoadLibrary(a) #define PLUGIN_GETADR(a, b) GetProcAddress(a, b) #define PLUGIN_UNLOAD(a) FreeLibrary(a) // ... #elif defined(LINUX) #define PLUGIN_HANDLE void *; #define PLUGIN_LOAD(a) dlopen(a, RTLD_LAZY) #define PLUGIN_GETADR(a, b) dlsym(a, b) #define PLUGIN_UNLOAD(a) dlclose(a) // ... #endif // verwendung PLUGIN_HANDLE m_Handle = (PLUGIN_HANDLE)PLUGIN_LOAD(szName); // usw usw
ich finde das ganz gut. ist das nicht ehr eine stil frage ? oder spricht irgendwas gravierendes dagegen ?
-
nagut jetzt habt ihr mich überzeugt.
werd mir heut abend mal policy's anschauem.
-
Die Namenskollisionen sind nur ein Problem, man kann bei sowas natürlich auch auftretende Fehler nicht behandeln, weil dlopen und LoadModule verschiedene Fehlercodes haben, usw.
Nur kommen mir Policys für das Plugin-Beispiel auch ein *klein* wenig übertrieben vor. Wofür templates ins Boot holen, für Sachen, die eh nur von Kompilierung zu Kompilierung unterschiedlich sein können? Innerhalb eines Projekts wird ja niemand Foo<LinuxPolicy> und Foo<WindowsPolicy> mischen wollen, was mit templates natürlich ginge.
namespace Plugin { #ifdef WIN32 typedef HMOBULE Handle; #else typedef void* Handle; #endif Handle load(const std::string&) ... }
Oder noch viel besser, eine Plugin-Klasse, deren private-Teil entweder per #ifdef ausgewählt oder durch das pimpl-Idiom einfach komplett nach draußen geschoben wird. Da muss man fürs Benutzen von Plugins nicht einmal implizit windows.h inkludieren und spart sich evtl. ne Menge Compilezeit.
On-Topic? Was ist das?