2011-11-10 16 views
5

Desidero utilizzare lo stesso approccio #ifdef utilizzato da WxWidgets, SDL ecc. L'unico problema è che non so come usarlo.Come si usa il preprocessore per creare una libreria multipiattaforma?

Dire che voglio creare una classe che disegna un rettangolo. Voglio che utilizzare cairo quando su piattaforme X11 (cioè Linux) e GDI su piattaforme Win32:

class graphics 
{ 
void drawRect(int x, int y, int w, int h) 
{ 
/*if on win32*/ 
HDC myHdc = ::BeginPaint(myHwnd,&mypstr); 
::Rectangle(myHdc,x,y,w,h); 
::EndPaint(myHwnd,&mypstr); 

/*if on x11*/ 
cairo_surface_t* s = cairo_xlib_surface_create(/* args */); 
cairo_t* c = cairo_create(s); 
cairo_rectangle(c,x,y,w,h); 
// etc. etc. 
} 
}; 

Come vorrei usare #ifdef o qualcos'altro per fare questo?

+0

Do not WxWidgets e SDL forniscono gli esempi di preprocessore che stai cercando? –

+1

(+1) Il concetto rappresentato dal titolo della domanda vale un upvote. Non avere una risposta, leggendo le risposte degli altri sviluppatori ... – umlcat

risposta

3

Se è possibile, preferiscono mettere metodi specifici di piattaforma in unità di traduzione separate. Ciò consentirà un codice più leggibile & più facile da sviluppare e mantenere. IMHO, il processo di compilazione dovrebbe determinare quali moduli specifici della piattaforma incorporare.

Ad esempio:
rectangle.hpp:

struct Rectangle 
{ 
    void draw(int upper_left_corner_x, int upper_left_corner_y, 
      unsigned int length, unsigned int width); 
} 

Ora i file specifici della piattaforma:
rectangle_wx_widgets.cpp:

#include "rectangle.hpp" 
void 
Rectangle :: 
draw(int upper_left_corner_x, int upper_left_corner_y, 
      unsigned int length, unsigned int width) 
{ 
// wxWidgets specific code here. 
} 

rectangle_qt.cpp:

#include "rectangle.hpp" 
void 
Rectangle :: 
draw(int upper_left_corner_x, int upper_left_corner_y, 
      unsigned int length, unsigned int width) 
{ 
// QT specific code here. 
} 

main.cpp:

#include "rectangl.hpp" 
int main(void) 
{ 
    Rectangle r; 
    r.draw(50, 50, 10, 5); 
    return 0; 
} 

Il codice di cui sopra non ha direttive del preprocessore condizionali. La funzione main disegna un rettangolo indipendentemente dalla piattaforma. Le specifiche della piattaforma sono state rimosse da main perché non sono importanti. Quindi, senza modificare gli switch del compilatore o preoccuparsi di quale identificatore del preprocessore è definito, si può vedere che main disegna in modo incondizionato un rettangolo.

Per disegnare utilizzando una piattaforma wxWidgets, il processo di generazione utilizza rectangle_wx_widgets.cpp. Per disegnare usando QT, dovrebbe essere usato il file rectangle_qt.cpp, ma non entrambi. Come mostra questa illustrazione, non viene modificato alcun codice per produrre codice per entrambe le piattaforme.Si potrebbe adattare il processo di compilazione in modo che il comando build wxWidgets includa le unità di traduzione appropriate. In questo modo il passaggio di un parametro al processo di generazione genera l'eseguibile specifico della piattaforma.

+0

Sono d'accordo che la tua soluzione sembra più pulita. (+1), ma sento uno o due problemi, e se voglio cambiare qualcosa nell'intestazione? ad esempio se windows 'typedef HDC painter' else' typedef cairo_t painter' – ApprenticeHacker

+0

@IntermediateHacker: I file specifici della piattaforma possono includere intestazioni specifiche della piattaforma. Nel tuo esempio di risposta, crei un metodo generico 'paint' che è specifico per la struttura. Ci sarebbe un 'paint' per Windows API e un' paint' per Cairo. E propagare questo il più lontano possibile. L'obiettivo è ridurre la quantità di direttive condizionali del preprocessore. –

+0

Grazie. La tua soluzione funziona perfettamente. – ApprenticeHacker

0

Sono abbastanza diretti.

volete qualcosa di simile:

#ifndef WIN32 
// something for X11 
#else 
// something for Windows 
#endif 

E 'solo una questione di sapere i condizionali di default (come Win32 in questo caso). Ci sono condizionali per il sistema operativo, i compilatori e molti elementi di portabilità.

In questo caso, esaminare il file di intestazione windows.h. Potrebbe contenere più informazioni per te.

+0

probabilmente vorrai 'ifdef' in modo che tu possa supportare più di due. –

0

Ci sono variabili piattaforma predefinite come Win32 che è possibile utilizzare:

#ifdef WIN32 
.... 
#else 
.... 
#endif 
2

multipiattaforma non significa solo unix vs windows. inoltre, le direttive cpp predefinite possono essere dipendenti dal compilatore. aumenta il codice sorgente della selezione della piattaforma è un buon inizio per vedere come questo può essere fatto nel modo giusto. vedi config/select_platform_config.hpp si potrebbe anche voler dare un'occhiata a cmake che definisce alcune platform dependent defines

+0

scusa per l'implicazione "unix vs windows", non era previsto. Volevo solo spiegare il punto. – ApprenticeHacker

+1

no pun intended, stavo solo rendendo il mio punto – user237419

+0

nessun problema allora. (+1) :) – ApprenticeHacker

0

C'è un trucco per farlo che non vedo altri menzionare (edit: sysfault accennato). Ci sono due concetti diversi al lavoro qui. I comandi del preprocessore variano in base al target e al compilatore. Vale a dire, per dire che il compilatore che si sta utilizzando (GCC vs MSVC)

#ifdef _MSC_VER 
    /*MSVC*/ 
#endif 
#ifdef __GNUC__ 
    /*GCC*/ 
#endif 

MSVC compilerà solo per gli obiettivi di Windows (per quanto ci riguarda oggi), e GCC si rivolge tutto. Per GCC, a dire che OS è targeting:

#ifdef __MINGW32__ 
    /* Windows */ 
#endif 
#ifdef BSD 
    /* BSD */ 
#endif 
#ifdef __linux__ 
    /* linux */ 
#endif 
#ifdef __APPLE__ 
    /* OSX */ 
#endif 

Inoltre, a volte è necessario sapere qualcosa di specifico per il processore, ma non il sistema operativo:

__CHAR_UNSIGNED__ 
    GCC defines this macro if and only if the data type char is unsigned on the target machine. 
__BYTE_ORDER__ 
__ORDER_LITTLE_ENDIAN__ 
__ORDER_BIG_ENDIAN__ 
__ORDER_PDP_ENDIAN__ 
    __BYTE_ORDER__ is defined to one of the values __ORDER_LITTLE_ENDIAN__, __ORDER_BIG_ENDIAN__, or __ORDER_PDP_ENDIAN__ to reflect the layout of multi-byte and multi-word quantities in memory. 
__LP64__ 
_LP64 
    These macros are defined, with value 1, if (and only if) the compilation is for a target where long int and pointer both use 64-bits and int uses 32-bit. 

E molti molti altri

Problemi correlati