2009-03-08 15 views
8

Estoy compilando una biblioteca estática de C++ y como todas las clases tienen plantillas, las definiciones de clase y las implementaciones están todas en archivos de encabezado. Como resultado, parece (en Visual Studio 2005) que necesito crear un archivo .cpp que incluya todos los otros archivos de encabezado para que se compile correctamente en la biblioteca.¿Compila un C++ .lib con solo archivos de encabezado?

¿Por qué es esto?

Respuesta

8

El compilador no compila los archivos de encabezado, ya que están destinados a ser incluidos en los archivos fuente. Antes de realizar cualquier compilación, el preprocesador toma todo el código de cualquier archivo de encabezado incluido y lo coloca en los archivos de origen donde están incluidos, en la misma ubicación en la que están incluidos. Si el compilador también debe compilar los archivos de cabecera, tendría, por ejemplo, múltiples definiciones en muchas cosas.

ejemplo, esto es lo que ve el preprocesador:

[foo.h] 
void foo(); 

-

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

int main() 
{ 
    foo(); 
} 

Y esto es lo que el compilador ve:

[mysource.cpp] 
void foo(); 

int main() 
{ 
    foo(); 
} 
0

Piense Biblioteca de plantillas estándar. Sus clases con plantillas se compilarán cuando las utilice en otro proyecto.

0

Lo que otros han dicho es cierto con respecto a las plantillas que no se compilan en la biblioteca. Sin embargo, vale la pena obligarlos a ser vistos por el compilador (por #incluyendo ellos en un archivo .cpp) ya que de esta manera se verificarán al menos la sintaxis.

+0

Le conviene más escribir un conjunto de pruebas unitarias que forzar la creación de una biblioteca estática que no necesita existir. Después de todo, muchos compiladores perderán errores muy básicos en el código de plantilla si no se llama al código de esa plantilla. – Tom

2

En C++, las plantillas son solo una meta-definición de una clase real. Cuando compila una clase con plantilla, el compilador realmente genera el código de la clase real sobre la marcha para el tipo particular de datos pasados ​​(la plantilla es solo un "patrón" para copiar).

p. Ej. Si usted tiene el siguiente código

 

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(); 
} 
 

Lo que el compilador realmente ve es

 

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(); 
} 
 

Como podrán ver, el compilador no sabe en realidad lo que el código para generar, hasta que realmente se declara una instancia de tu plantilla. Esto significa que la plantilla no se puede compilar en una biblioteca, porque no sabe en qué terminará realmente la plantilla. La buena noticia sobre esto es que en realidad no necesita que se compile ni se incluya CUALQUIER biblioteca si solo distribuye los archivos de encabezado que incluyen la definición de la plantilla.

Además, como nota al margen, el comando del compilador previo '#include' simplemente le dice al precompilador que reemplace el '#include' con todo lo de ese archivo.

1

Intentas crear algo innecesario. La mayoría de las bibliotecas de C (y todas las librerías de C++) se distribuyen en dos porciones:

  • Interface (foo.h)
  • Implementación (foo.lib)

Para C código de la plantilla ++, toda su biblioteca debe ser compilado por sus usuarios finales, porque así es como funcionan las plantillas. No hay razón para proporcionar una biblioteca precompilada. En este caso, su puede pensar en su distribución biblioteca como esta:

  • Interface (foo.h)
  • Implementación (foo-inl.h)

Como dijo Niel anteriormente, es útil tener implementaciones sólo para su propios propósitos de prueba, y es probable que valga la pena distribuirlos con la biblioteca misma. Por lo tanto, debe tener un conjunto separado de pruebas unitarias que ejerzan su código; pero esas pruebas no deberían ser parte de la biblioteca en sí.

0

No necesita generar un archivo .lib si todas sus clases son plantillas, eche un vistazo a un impulso o el stlport no tienen una .lib que distribuyan [1].

Las plantillas se compilan cuando se usan.

[1] Estrictamente hablando, distribuyen bibliotecas para las funciones más avanzadas como expresiones regulares, iostream, etc., pero las bibliotecas auxiliares son utilizadas por otras plantillas, las plantillas por sí mismas no se distribuyen en forma de biblioteca.

2

Si todo su código está en archivos .h, entonces no necesita compilar una biblioteca estática para usar el código.

Todo el código está disponible para el uso de la biblioteca en el tiempo de compilación, por lo que no se necesita nada en el tiempo del enlace.

1

Si su biblioteca está implementada en archivos de encabezado, no necesita construir ningún archivo binario para usarla. Eso dijo. Normalmente creo un archivo .cpp durante mi fase de desarrollo inicial de la biblioteca de solo encabezado. ¿Por qué? Los compiladores no intentan compilar o incluso analizar su plantilla hasta que realmente se esté utilizando. Tener un archivo .cpp y tener algún código ficticio allí para crear instancias de las plantillas me ayuda a encontrar errores de sintaxis antes durante el desarrollo. Así que puedo agregar un código de plantilla, compilación de hits, corregir errores de sintaxis, agregar más código, compilar ... etc. Si intentas buscar algún error de sintaxis después de agregar cientos de líneas de código, sabrás a qué me refiero. Una vez que mi biblioteca esté lista para la prueba unitaria, eliminaré el archivo .cpp y confiaré en los testículos de la unidad para impulsar mi desarrollo.

Además, si solo compila su código usando VC++, una cosa que debe tener en cuenta es que VC++ no intenta compilar todas las funciones de miembros de la plantilla hasta que realmente se use. Por ejemplo:

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 

El f() compilará bien con VC++ 2003, g ++ cogerá el error de sintaxis. Creo que VC8 y VC9 también tienen el mismo problema.

Cuestiones relacionadas