2013-10-17 3 views
26
gcc (GCC) 4.7.2 

Ciao,Creazione di una libreria portabile per l'esecuzione su Linux e Windows

sto creando una libreria condivisa che compilerà su Linux e una dll che compilerà su Windows utilizzando lo stesso codice sorgente. Quindi sto creando una libreria portatile per Linux e Windows.

Nel mio file di intestazione per la libreria è presente cioè module.h

#ifdef __cplusplus 
extern "C" { 
#endif 

#ifdef _WIN32 
#define LIB_INTERFACE(type) EXTERN_C __declspec(dllexport) type 
#else 
#define LIB_INTERFACE(type) type 
#endif 

LIB_INTERFACE(int) module_init(); 

#ifdef __cplusplus 
} 
#endif 

In origine ho il seguente cioè module.c

#include "module.h" 

LIB_INTERFACE(int) module_init() 
{ 
    /* do something useful 
    return 0; 
} 

E nella mia applicazione di test che collegherà e utilizzare questa module.so ho questo:

#include "module.h" 

int main(void) 
{ 
    if(module_init() != 0) { 
    return -1; 
    } 
    return 0; 
} 

1) è quello che ho fatto sopra è una corretta ESECUZIONE creazione di una libreria portatile per linux e windows?

2) Mi sto solo chiedendo come ho avvolto le funzioni in extern "C" in modo che questa libreria possa essere chiamata da un programma che è stato compilato in C++. Ho ancora bisogno di questo EXTERN_C il seguente:

#define LIB_INTERFACE(type) EXTERN_C __declspec(dllexport) type 

3) Qual è lo scopo della EXTERN_C?

Molte grazie in anticipo,

+5

È una macro che si risolve in "extern" C "' per la compilazione C++ e "extern" o nulla per la compilazione C. (Onestamente non riesco a ricordare quale, ma evidenziarlo e premere F12, dovrebbe portarti alla definizione attuale). È * lo scopo * è di istruire il compilatore per rimuovere il manichino di nome C++ dal simbolo che viene esportato dalla libreria. E no, se hai già l'intero header avvolto in "extern" C "{" puoi eliminarlo (assumendo che gcc capisca lo stesso block-extern-C, e di nuovo, onestamente non ricordo se lo fa). – WhozCraig

+1

Leggete [questo] (http://gcc.gnu.org/wiki/Visibility). Potresti voler usare questo meccanismo. –

+0

@ ant2009 La libreria deve essere utilizzata da C oppure è possibile limitarla a C++ 11? –

risposta

20

Questo è un tipico modo per esportare un API DLL per Windows e ancora supportare Linux:

#ifdef __cplusplus 
extern "C" { 
#endif 

#ifdef _WIN32 
# ifdef MODULE_API_EXPORTS 
# define MODULE_API __declspec(dllexport) 
# else 
# define MODULE_API __declspec(dllimport) 
# endif 
#else 
# define MODULE_API 
#endif 

MODULE_API int module_init(); 

#ifdef __cplusplus 
} 
#endif 

Nella fonte DLL:

#define MODULE_API_EXPORTS 
#include "module.h" 

MODULE_API int module_init() 
{ 
    /* do something useful */ 
    return 0; 
} 

vostra fonte applicazione è corretta.

Utilizzando il modello precedente, in Windows la DLL esporterà l'API mentre l'applicazione la importerà. Se non su Win32, la decorazione __declspec viene rimossa.

Poiché l'intestazione copre l'intera interfaccia in extern "C", non è richiesta l'utilizzo della macro EXTERN_C su ciascuna interfaccia. extern "C" viene utilizzato per indicare al linker di utilizzare il collegamento C anziché C++. Il collegamento C è standard tra i compilatori, mentre C++ no, limitando l'uso di una DLL all'applicazione creata con lo stesso compilatore.

Non è necessario integrare il tipo di ritorno nella macro API.

13

extern "C" fondamentalmente significa che si sta dicendo al compilatore di non storpiare il tuo nome della funzione. Mangling è il processo di "codifica" dei nomi di funzioni per l'esecuzione successiva ed è molto diverso in C e C++ in quanto C++ può avere funzioni diverse con lo stesso nome (tramite sovraccarico ecc ...).

In C++ source, what is the effect of extern "C"?

Una volta compilati queste funzioni possono essere richiamate da nessuna parte, ma si potrebbe desiderare di essere sicuri di che tipo di biblioteca che si sta creando (statico o dinamico) prima di iniziare.

Inoltre, si consiglia di non utilizzare DEFINES come si fa nello stesso file a scopi di portabilità a causa dei problemi di manutenzione o di leggibilità che si potrebbero verificare in seguito durante lo sviluppo. Vorrei creare un file base che definisca un'interfaccia completamente portatile per WIN e UNIX, quindi creare altre due librerie che implementano l'interfaccia ma per piattaforme diverse.

Ad esempio si può avere: AbstractInterface.h, WinInterface.h, UnixInterface.h

Poi compilare solo quelle che ti servono a seconda della piattaforma.

3

A causa del concetto di polimorfismo specifico del linguaggio C++, tutte le funzioni definite in C++ vengono alterate. Ad esempio, per creare nomi univoci per ciascuna funzione sovrascritta, il "compilatore" decora i nomi delle funzioni.

Poiché il nome mangling è gestito dal "compilatore" e non esistono specifiche per definire rigorosamente le regole di manomissione del nome, ciascun compilatore decora i nomi in modi diversi. In poche parole, i compilatori gcc e msvc creano diverse firme di funzioni per lo stesso codice.puoi leggere ulteriori informazioni sulla manomissione dei nomi nell'articolo wiki here.

Il file module.h indica semplicemente al compilatore di utilizzare il nome di stile c mangling o di non modificare affatto. a causa di questa direttiva, la libreria che è compilata da gcc può essere utilizzata per collegarsi a un binario che è scritto in Visual Studio. Questo ti aiuterà a distribuire i binari della tua libreria invece del codice sorgente.

D'altra parte, se non si utilizza la direttiva EXTERN_C, la libreria e il progetto che si collega alla libreria devono essere compilati utilizzando lo stesso compilatore. per esempio, devi usare gcc per la compilazione di linux e msvc per la compilazione di Windows sia per la libreria che per il progetto che si collega a quella libreria.

+0

's/polimorfismo/sovraccarico /' –

+0

il polimorfismo è un superset di sovraccarico; p – madrag

8

Per Linux, gcc senza -fvisibility = hidden renderà le funzioni esportate per impostazione predefinita, ad eccezione delle funzioni statiche.

Con -fvisibility = nascosto, gcc farà nessun funzioni esportate per impostazione predefinita, se non che le funzioni decorate da

__attribute__ ((visibility ("default"))) 

per Windows, funzioni esportate decorate da

__attribute__ ((dllexport)) 

quando si utilizzano le funzioni esportate , devono essere decorati da

__attribute__ ((dllimport)) 

I macro in i tuoi post

__declspec(dllexport) 

sono supportati da MSVC.

Così i Linux e Windows macro incrociate sono i seguenti:

#if defined _WIN32 || defined __CYGWIN__ || defined __MINGW32__ 
    #ifdef BUILDING_DLL 
    #ifdef __GNUC__ 
     #define DLL_PUBLIC __attribute__ ((dllexport)) 
    #else 
     #define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. 
    #endif 
    #else 
    #ifdef __GNUC__ 
     #define DLL_PUBLIC __attribute__ ((dllimport)) 
    #else 
     #define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. 
    #endif 
    #endif 
    #define DLL_LOCAL 
#else 
    #if __GNUC__ >= 4 
    #define DLL_PUBLIC __attribute__ ((visibility ("default"))) 
    #define DLL_LOCAL __attribute__ ((visibility ("hidden"))) 
    #else 
    #define DLL_PUBLIC 
    #define DLL_LOCAL 
    #endif 
#endif 
  • Assicurarsi che i progetti oggetto o DLL condivise devono essere compilati con -DBUILDING_DLL.
  • Il progetto che dipende dal vostro oggetto condiviso o DLL deve essere compilato senza -DBUILDING_DLL

Per i più particolari, si prega di leggere http://gcc.gnu.org/wiki/Visibility

2

Invece di scrivere un file di intestazione te stesso, è anche possibile lasciate che CMake generare uno per il compilatore edificio utilizzando CMake's generate_export_header come questo (esempi tratti dalla pagina collegata):

add_library(libfoo foo.cpp) 
generate_export_header(libfoo)
#include "libfoo_export.h" 
class LIBFOO_EXPORT FooClass { 
    int bar; 
};