views:

1701

answers:

4

I'm trying to link to a shared library with a template class, but it is giving me "undefined symbols" errors. I've condensed the problem to about 20 lines of code.

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

This is how I compile the library:

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

And the main program:

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

Only to get the following errors:

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

If I remove the 'template' stuff in shared.h/cpp, and replace them with just 'int', everything works fine. Also, if I just copy&paste the template class code right into main.cpp, and don't link to the shared library, everything works as well.

How can I get a template class like this to work through a shared library?

I'm using MacOS 10.5 with GCC 4.0.1.

+5  A: 

Template function definitions must reside in header files. Move the definitions from shared.cpp to shared.h.

So, you can't compile this to a shared library and then link to it. It just doesn't work like that.

rlbond
So it's impossible to have a shared library with templates?
nolk
On the contrary, it is ridiculously easy. Just put it in a header file, and share that. You don't even need a compiler then ;) Templates don't get compiled until they are instantiated, so if you put a template in a .cpp file and compile that as a shared library, the template code is simply removed. The template definition has to be visible to the user.
jalf
+2  A: 

You need to include the implementation of the template classes in the header files as well. This is a constraint of templates in C++. So either include shared.cpp from main (#include ) or just move the code from shared.cpp in shared.h

Cătălin Pitiș
+1  A: 

The compiler has to see all the code for a template, so it can generate the appropriate code for the actual type you want to use. So you should place all the code in your .h. file.

nos
+4  A: 

In addition to the other answers, you can explicitly instantiate template classes. This is only useful if you know beforehand what types the template parameters may assume. You instantiate the template with all these types in the library.

For your example to compile, just add this line to shared.cpp:

template class myclass<int>;

This instantiates the template with Type=int and places the instantiated code in the shared library. Add as many explicit instantiations as you need, for all the types you need.

Again, if you want to be able to instantiate the template with any arbitrary Type parameter, then you must add the definitions to the header file, so that the compiler knows the source code of the template when instantiating it in other compilation units.

Juliano
This is exactly what I was looking for. Thank you so much!For reference to other people with the same problem: Add the template instantiation lines at the END of the shared.cpp file.
nolk