2009-06-20 12 views
31

Estoy tratando de vincular a una biblioteca compartida con una clase de plantilla, pero me está dando errores de "símbolos indefinidos". He condensado el problema en aproximadamente 20 líneas de código.Biblioteca compartida C++ con plantillas: error de símbolos no definidos

SHARED.H

template <class Type> class myclass { 
    Type x; 
public: 
    myclass() { x=0; } 
    void setx(Type y); 
    Type getx(); 
}; 

shared.cpp

#include "shared.h" 
template <class Type> void myclass<Type>::setx(Type y) { x = y; } 
template <class Type> Type myclass<Type>::getx() { return x; } 

main.cpp

#include <iostream> 
#include "shared.h" 
using namespace std; 

int main(int argc, char *argv[]) { 
    myclass<int> m; 
    cout << m.getx() << endl; 
    m.setx(10); 
    cout << m.getx() << endl; 
    return 0; 
} 

Así es como puedo compilar la lib ral:

g++ -fPIC -c shared.cpp -o shared.o 
g++ -dynamiclib -Wl,-dylib_install_name -Wl,libshared.dylib -o libshared.dylib shared.o 

Y el programa principal:

g++ -c main.cpp 
g++ -o main main.o -L. -lshared 

Sólo para obtener los siguientes errores:

Undefined symbols: 
"myclass<int>::getx()", referenced from: 
    _main in main.o 
    _main in main.o 
"myclass<int>::setx(int)", referenced from: 
    _main in main.o 

Si quito las cosas 'plantilla' en shared.h/cpp, y reemplazarlos con solo 'int', todo funciona bien. Además, si solo copio & pego el código de clase de la plantilla directamente en main.cpp, y no enlazo a la biblioteca compartida, todo funciona también.

¿Cómo puedo obtener una clase de plantilla como esta para que funcione a través de una biblioteca compartida?

Estoy usando MacOS 10.5 con GCC 4.0.1.

+0

duplicado: http://stackoverflow.com/questions/999358/undefined-symbols-linker-error-with-simple-template-class/999383#999383 –

Respuesta

34

Además de las otras respuestas, puede crear instancias explícitas de clases de plantilla. Esto solo es útil si conoce de antemano qué tipos pueden asumir los parámetros de la plantilla. Crea una instancia de la plantilla con todos estos tipos en la biblioteca.

Para su ejemplo para compilar, sólo tiene que añadir lo siguiente al final de shared.cpp:

// Instantiate myclass for the supported template type parameters 
template class myclass<int>; 
template class myclass<long>; 

Esto crea la instancia de la plantilla con el Tipo = int y coloca el código instanciado en la biblioteca compartida. Agregue tantas instancias explícitas como necesite, para todos los tipos que necesite.

Una vez más, si usted quiere ser capaz de crear una instancia de la plantilla con cualquier parámetro de tipo arbitrario, entonces debe añadir las definiciones en el fichero de cabecera, para que el compilador conoce el código fuente de la plantilla al crear instancias en otras unidades de compilación.

+2

Esto es exactamente lo que estaba buscando. Muchas gracias! Para referenciar a otras personas con el mismo problema: agregue las líneas de creación de instancias de plantilla al FIN del archivo shared.cpp. – nolk

+0

Eso es genial, pero ¿y si tengo _dos archivos fuente_, ninguno como lo hace OP? ¿Dónde debería agregar estas líneas? – ForceBru

+0

@ForceBru: en cualquiera de los dos. Sugiero el archivo de origen que corresponde al archivo de encabezado que define la plantilla. – Cookie

16

Las definiciones de funciones de plantilla deben residir en archivos de encabezado. Mueva las definiciones de shared.cpp a shared.h.

Por lo tanto, no puede compilar esto en una biblioteca compartida y luego vincularlo. Simplemente no funciona así.

+2

Por lo tanto, es imposible tener una biblioteca compartida con las plantillas? – nolk

+2

Por el contrario, es ridículamente fácil. Simplemente colócalo en un archivo de encabezado y compártelo. Ni siquiera necesita un compilador;) Las plantillas no se compilan hasta que se crean instancias, de modo que si coloca una plantilla en un archivo .cpp y la compila como una biblioteca compartida, simplemente se elimina el código de la plantilla. La definición de la plantilla debe ser visible para el usuario. – jalf

+0

¿Está absolutamente seguro? Porque actualmente estoy trabajando con una biblioteca compartida que crea instancias de las plantillas durante su compilación. El código es invisible para el usuario, pero se puede usar. Pueden surgir problemas de compilación cuando combina especializaciones que necesitan generación y otras que ya están generadas. Pero a excepción de eso, funciona bien. Probado con g ++ y msvc. – Michael

5

También debe incluir la implementación de las clases de plantilla en los archivos de encabezado. Esta es una restricción de plantillas en C++. Por lo tanto, incluya shared.cpp desde main (#include) o simplemente mueva el código desde shared.cpp en shared.h

1

El compilador debe ver todo el código de una plantilla, para que pueda generar el código apropiado para el tipo real que desea usar Por lo tanto, debe colocar todo el código en su .h. archivo.

Cuestiones relacionadas