2009-03-08 4 views
8

Sto compilando una libreria statica C++ e poiché tutte le classi sono basate su modelli, le definizioni di classe e le implementazioni sono tutte nei file di intestazione. Di conseguenza, sembra (in Visual Studio 2005) che ho bisogno di creare un file .cpp che include tutti gli altri file di intestazione perché possa essere compilato correttamente nella libreria.Compilando un file .lib in C++ con solo file di intestazione?

Perché è questo?

risposta

8

Il compilatore non compila i file di intestazione poiché questi sono destinati a essere inclusi nei file di origine. Prima di qualsiasi compilazione in corso, il preprocessore prende tutto il codice da qualsiasi file di intestazione incluso e inserisce tale codice nei file di origine in cui sono inclusi, nella posizione in cui sono inclusi. Se il compilatore dovesse compilare anche i file di intestazione, avresti per esempio più definizioni su molte cose.

esempio, questo è ciò che il preprocessore vede:

[foo.h] 
void foo(); 

-

[mysource.cpp] 
#include "foo.h" 

int main() 
{ 
    foo(); 
} 

E questo è ciò che il compilatore vede:

[mysource.cpp] 
void foo(); 

int main() 
{ 
    foo(); 
} 
0

Pensate Libreria di modelli standard. Le tue classi basate su modelli verranno compilate quando le utilizzi in un altro progetto.

0

Ciò che altri hanno detto è vero per i modelli non compilati nella libreria. Tuttavia, vale ancora la pena costringerli a essere visti dal compilatore (includendoli in un file .cpp), in questo modo saranno almeno controllati per la sintassi.

+0

È meglio scrivere un set di test unitari piuttosto che forzare la creazione di una libreria statica che non ha bisogno di esistere. Dopo tutto, molti compilatori mancheranno errori di base sul codice del template se quel codice template non viene chiamato. – Tom

2

In C++, i modelli sono solo una meta-definizione di una classe reale. Quando si compila una classe basata su modelli, il compilatore genera effettivamente il codice per la classe effettiva al volo per il particolare tipo di dati passati (il modello è solo un "modello" da copiare).

ad es. Se avete il seguente codice

 

struct MyTemplate 
{ 
private: 
    float MyValue; 

public: 
    float Get() { return MyValue; } 
    void Set(float value) { MyValue = value; } 
}; 

void main() 
{ 
    MyTemplate v1; 
    MyTemplate v2; 
    v1.Set(5.0f); 
    v2.Set(2); 
    v2.Get(); 
} 
 

Ciò che il compilatore vede in realtà è

 

struct CompilerGeneratedNameFor_MyTemplate_float 
{ 
private: 
    float MyValue; 

public: 
    float Get() { return MyValue; } 
    void Set(float value) { MyValue = value; } 
}; 

struct CompilerGeneratedNameFor_MyTemplate_int 
{ 
private: 
    int MyValue; 

public: 
    int Get() { return MyValue; } 
    void Set(int value) { MyValue = value; } 
}; 

void main() 
{ 
    CompilerGeneratedNameFor_MyTemplate_float v1; 
    CompilerGeneratedNameFor_MyTemplate_int v2; 
    v1.Set(5.0f); 
    v2.Set(2); 
    v2.Get(); 
} 
 

Come probabilmente si può vedere, il compilatore in realtà non sa quale codice per generare, fino a che realmente dichiarare un'istanza del tuo modello. Ciò significa che il modello non può essere compilato in una libreria, perché non sa come sarà effettivamente il template. La buona notizia a questo proposito è che in realtà non è necessario che QUALSIASI libreria venga compilata o inclusa se si distribuiscono semplicemente i file di intestazione che includono la definizione del modello.

Inoltre, come nota a margine, il comando di pre-compilatore "#include" in realtà dice al pre-compilatore di sostituire "#include" con qualsiasi cosa proveniente da quel file.

1

Stai cercando di creare qualcosa di non necessario. La maggior parte delle librerie C (e di tutte le librerie C++) vengono distribuiti come due porzioni:

  • Interface (foo.h)
  • Attuazione (foo.lib)

Per codice C modello ++, tutta la vostra libreria deve essere compilata dai tuoi utenti finali, perché è così che funzionano i modelli. Non c'è motivo di fornire una libreria precompilata. In questo caso, la tua può pensare della distribuzione biblioteca come questa:

  • Interface (foo.h)
  • Attuazione (foo-inl.h)

Come ha detto Niel sopra, è utile avere implementazioni solo per il vostro proprio scopo di test, ed è probabilmente utile distribuire quelli con la libreria stessa. Quindi dovresti avere una suite separata di test unitari che eserciti il ​​tuo codice; ma quei test non dovrebbero far parte della biblioteca stessa.

0

Non è necessario generare un .lib se tutte le classi sono modelli, dare un'occhiata a un boost o allo stlport che non hanno un .lib che distribuiscono [1].

I modelli vengono compilati quando vengono utilizzati.

[1] In senso stretto distribuiscono librerie per le funzioni più avanzate come le espressioni regolari, iostream ecc., Ma le librerie ausiliarie sono utilizzate da altri modelli, i modelli da soli non sono distribuiti in forma di libreria.

2

Se tutto il codice è in file .h, non è necessario compilare una libreria statica per utilizzare il codice.

Tutto il codice è disponibile per l'utilizzo della libreria al momento della compilazione, quindi non è necessario nulla al momento del collegamento.

1

Se la libreria è implementata tutti nei file di intestazione, non è necessario creare alcun file binario per utilizzarlo. Detto ciò. Generalmente creo un file .cpp durante la mia fase di sviluppo iniziale della sola libreria di intestazione. Perché? I compilatori non tentano di compilare o analizzare il tuo modello fino a quando non viene effettivamente utilizzato. Avere un file .cpp e avere un codice fittizio per istanziare i modelli mi aiuta a trovare errori di sintassi prima durante lo sviluppo. Quindi posso aggiungere un po 'di codice template, compilare la compilazione, correggere l'errore di sintassi, aggiungere altro codice, compilare ... ecc. Se provate a cercare qualche errore di sintassi stupido dopo aver aggiunto centinaia di righe di codice, saprete cosa intendo. Una volta che la mia libreria è pronta per il test delle unità, rimuoverò il file .cpp e farò affidamento sui test delle unità per guidare il mio sviluppo.

Inoltre, se si compila il codice solo utilizzando VC++, una cosa di cui si deve essere a conoscenza è che VC++ non tenta di compilare tutte le funzioni membro del modello finché non viene effettivamente utilizzato. Per esempio:

template <typename T> 
class MyTemplate 
{ 
public: 
    MyTemplate() {} // default constructor 

    MyTemplate(int) { 
     1 = 2 
     // other syntax error code here 
    } 
}; 

void f() { MyTemplate<int> myt(); } // compile fine in VC 
void g() { MyTemplate<int> myt(1); } // syntax error 

Il f() compilerà bene con VC++ 2003 g ++ cattura l'errore di sintassi. Penso che anche VC8 e VC9 abbiano lo stesso problema.