C++ Shared Library with Templates: Undefined symbols error - c++

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.

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 the following to the end of shared.cpp:
// Instantiate myclass for the supported template type parameters
template class myclass<int>;
template class myclass<long>;
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.

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.

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

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.

Related

C++ template symbol visibility differences between macOS and Linux?

What are the rules and rule differences between macOS and linux, for the visibility
of internal/external symbols in shared libraries? In particular, the external
visibility of C++ template instantiations. It looks like gcc on linux allows linking to 'unexported' symbols....
Example:
If I have an templated class with a virtual function "interface.h":
template <int T>
class SomeTemplate {
public:
virtual void test_fun(void) const;
};
Whose function is defined in a private source file inside a library somelib.so
"somelib.cpp" (compiled with g++ -fPIC -shared -o libsomelib.so somelib.cpp),
where the library also uses the instantiated template in other functions/class methods:
#include <iostream>
#include "interface.h"
template <int T>
void SomeTemplate<T>::test_fun(void) const
{
std::cout << "Test function " << T << std::endl;
}
void something(void)
{
SomeTemplate<1>().test_fun();
}
// Explicit template instantiation required for macOS - see below
// template class SomeTemplate<1>;
When compiled on linux the template symbols end up in the "W" section, as
determined by nm -C libsomelib.so | grep SomeTemplate:
0000000000000bc4 W SomeTemplate<1>::SomeTemplate()
0000000000000bc4 W SomeTemplate<1>::SomeTemplate()
0000000000000be0 W SomeTemplate<1>::test_fun() const
0000000000201060 V typeinfo for SomeTemplate<1>
0000000000000c90 V typeinfo name for SomeTemplate<1>
0000000000201040 V vtable for SomeTemplate<1>
And using this in a program/library that links to this somelib works fine, e.g. "program.cpp" (g++ -o testlib libsomelib.so program.cpp):
#include "interface.h"
int main(int argc, char const *argv[])
{
SomeTemplate<1>().test_fun();
return 0;
}
However, compiling on macOS (and manually demangling) puts the symbols required
by the program in what man nm calls the "local (non-external) text section":
0000000000000f70 t SomeTemplate<1>::SomeTemplate()
0000000000000fe0 t SomeTemplate<1>::SomeTemplate()
0000000000000f90 t SomeTemplate<1>::test_fun() const
00000000000020d8 D typeinfo for SomeTemplate<1>
0000000000001f50 S typeinfo name for SomeTemplate<1>
00000000000020c0 d vtable for SomeTemplate<1>
And so it seems that being a private symbol, the linking of the program fails with undefined symbols:
Undefined symbols for architecture x86_64:
"SomeTemplate<1>::test_fun() const"
It seems that the usage of the templated class inside the library isn't
enough - I need to explicitly declare the template instantiation e.g.
the commented line in somelib.cpp, then the symbol ends up in "T", visible, and the test program compiles and runs.
What's causing this difference in behaviour, what terminology do I have to use to understand this, and is there a way to get the platforms to match behaviour? Is the non-explicit instantiation just considered bad practice that was bound to break eventually?
(this is a large project I've joined where the upstream library developers aren't necessarily amenable to changes, and my predecessor evidently gave up and forced everything to static linking when compiling on macOS).

Is it possible to explicitly specialize template to match lambda?

Suppose I have a header wrapper.h:
template <typename Func> void wrapper(const Func func);
and a file wrapper.cpp containing:
#include "wrapper.h"
template <typename Func>
void wrapper(const Func func)
{
func();
}
And a file main.cpp containing:
#include "wrapper.h"
#include <iostream>
int main()
{
wrapper( [](){std::cout<<"hello."<<std::endl;} );
}
If I compile these together (e.g., cat wrapper.cpp main.cpp | g++ -std=c++11 -o main -x c++ -), I get no linker errors.
But if I compile them separately (e.g., g++ -std=c++11 -o wrapper.o -c wrapper.cpp && g++ -std=c++11 -o main main.cpp wrapper.o), I --- of course --- get a linker error:
Undefined symbols for architecture x86_64:
"void wrapper<main::$_0>(main::$_0)", referenced from:
_main in main-5f3a90.o
Normally, I could explicitly specialize wrapper and add something like this to wrapper.cpp:
template void wrapper<void(*)()>(void(*)())
But this particular template specialization doesn't work.
Is it possible to specialize a template on a lambda?
First, I assume you know about Why can templates only be implemented in the header file?
To your question:
Is it possible to specialize a template on a lambda?
Unfortunately No, template specializations work with exact match, and a lambda is a unique unnamed type. The problem is specializing for that type which you do not know.
Your best bet is to use std::function; or as you have done, then additionally force the lambda to be converted into a function pointer by adding +
int main()
{
wrapper(+[](){std::cout<<"hello."<<std::endl;} );
}
Full example:
#include <iostream>
template <typename Func>
void wrapper(const Func func)
{
std::cout << "PRIMARY\n";
func();
}
template <>
void wrapper<void(*)()>(void(*func)())
{
std::cout << "SPECIALIZATION\n";
func();
}
int main()
{
wrapper([](){std::cout<<"hello\n"<<std::endl;} );
wrapper(+[](){std::cout<<"world."<<std::endl;} );
}
This will print
PRIMARY
hello
SPECIALIZATION
world
Also, decltype facility wouldn't help, if it does, it will take away the flexibility of your need for lambda
Unfortunately you can't.
Your problem is that lambda types are randomly generated inside each compilation unit.
You can use functions across units because you can declare them in headers; then the linker will find the one with the correct name and type in the compilation unit which defines it. Same if you declare a variable, though less common. If the declaration resulted in a different type in each unit, this would fail and no interaction across units would be possible.
So if you try to make that lambda the "same" object in the two units (i.e. defining in header) the linking will fail because you cannot define the same object twice. And if you make them "different" objects (i.e. adding inline, or defining in source) they will have different types, so linking will fail to unify them as you would need to. Can't win.
Listen to : Why can templates only be implemented in the header file?
So when template definition is moved from file wrapper.cpp into headerfile wrapper.h, the wrapper() can be called by the suggested ways in main.cpp:
int main()
{
wrapper( [](){std::cout<<"hello"<<std::endl;} );
wrapper(+[](){std::cout<<"world"<<std::endl;} );
wrapper(std::function<void()>( [](){std::cout<<"best"<<std::endl;} ));
}

c++ linker error - 'undefined reference' to not inline defined functions

For few days I'm trying to compile one project written in C++ using Code::Blocks IDE (on Linux, Ubuntu 64-bit). Code is valid but there are some linker errors. I noticed that I get errors 'undefined reference' for functions which are not inline defined in classes and are in other files (class is i *.h file and definitions on these functions are in *.cpp). I tried to write my own Makefile but it didn't help.
Makefile:
all: project
project: main.o DList.o Person.o
g++ main.o DList.o Person.o -o project
main.o: main.cpp
g++ -c main.cpp
DList.o: include/DList.cpp
g++ -c include/DList.cpp
Person.o: include/Person.cpp
g++ -c include/Person.cpp
clean:
rm -rf *.o
I don't know what should I do although I read some about these errors on the net.
// EDIT
I changed Object.cpp and Object.h to Person.cpp and Person.h, moved *.cpp files to main directory and changed #include paths in *.cpp files.
Errors:
obj/Debug/main.o||In function `main':|
...main.cpp|19|undefined reference to `DListIterator<Person>::go(int)'|
...main.cpp|20|undefined reference to `std::basic_ostream<char, std::char_traits<char> >& operator<< <Person>(std::basic_ostream<char, std::char_traits<char> >&, DList<Person>&)'|
...main.cpp|21|undefined reference to `DList<Person>::~DList()'|
...main.cpp|21|undefined reference to `DList<Person>::~DList()'|
obj/Debug/main.o||In function `DList<Person>::insert(Person&)':|
...include/DList.h|45|undefined reference to `DList<Person>::insert(Person&, DListIterator<Person>&)'|
||=== Build finished: 5 errors, 0 warnings ===|
It makes no difference if I build starting make in command line or using Build function in Code::Blocks.
When I copied all code from *.cpp files to *.h files, compiler returned no errors, so I think that it's only linker problem.
It looks like you are attempting to separately compile a template. This is not possible in general, as a template will only be instantiated when it is used, and it is never used in the DList.cpp file. Try one of two things:
Move the definitions of the functions in DList into the header file (this is the normal way of doing things).
Put some explicit instantiations of DList in to the DList.cpp file. (Example: template class DList<Person>;)
Full example of the problem: Currently you have:
//DList.h
template<typename T>
class DList {
void insert(T& newPerson);
//etc
};
//DList.cpp
#include "DList.h"
//The when this is being compiled,
//the compiler does not know about Person,
//so it cannot instantiate this function.
template<typename T>
void DList<T>::insert(T& newPerson) {
//implementation
}
//etc
//main.cpp
#include "DList.h"
#include "Person.h"
int main() {
//When this is being compiled, it does not know the
//definition of the out-of-line functions in `DList`,
//so it cannot instantiate them.
DList<Person> people;
people.insert(Person("Joe"));
}
One possible fix is to remove DList.cpp and put the definitions in "DList.hpp":
//DList.hpp
template<typename T>
class DList {
void insert(T& newPerson) {
//implementation
}
~DList();
//etc
};
//the implementations can alternatively be
//placed outside the class, but in the header:
template<typename T>
DList<T>::~DList() {
//implementation
}
The other fix is to explicitly instantiate DList (in a compilation unit where the definitions are available):
//DList.cpp
#include "DList.h"
#include "Person.h"
template<typename T>
void DList<T>::insert(T& newPerson) {
//implementation
}
//Explicit instantiation:
template class DList<Person>;

Separate compiling with MinGW

Using this tutorial Makefile I want to build a simple program with a separate compiling, The main problem is that the IDE Eclpse Indigo C\C++ (prespective) or MinGW I cannot compile the files. The error which I get is :
undefined reference to double getAverage<int, 85u>(int (&) [85u])'
undefined reference to int getMax<int, 85u>(int (&) [85u])'
undefined reference to int getMin<int, 85u>(int (&) [85u])'
undefined reference to void print<int, 85u>(int (&) [85u])'
undefined reference to void sort<int, 85u>(int (&) [85u])'
undefined reference to void print<int, 85u>(int (&) [85u])'
The main.cpp file is this :
#include "Tools.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
int numbers[] = {1,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8,-2,7,14,5,6,16,8};
cout <<"Average = "<< getAverage(numbers) << endl;
cout <<"Max element = "<< getMax(numbers) << endl;
cout <<"Minimal element = "<< getMin(numbers) << endl;
print(numbers);
sort(numbers);
print(numbers);
return 0;
}
and I have a Tools.h file :
#ifndef TOOLS_H_
#define TOOLS_H_
#include <iostream>
int getBigger(int numberOne,int numberTwo);
template <typename T,size_t N> double getAverage(T (&numbers)[N]);
template <typename T,size_t N> T getMax(T (&numbers)[N]);
template <typename T,size_t N> T getMin(T (&numbers)[N]);
/**
* Implementing a simple sort method of big arrays
*/
template <typename T,size_t N> void sort(T (&numbers)[N]);
/**
* Implementing a method for printing arrays
*/
template <typename T,size_t N> void print(T (&numbers)[N]);
#endif
When you compile Tools.cpp your compiler has no idea about the template parameters that you have used in main.cpp. Therefore it compiles nothing related to this templates.
You need to include theses template definitions from the compilation unit that uses them. The file Tools.cpp is often renamed to something like Tools.inl to indicate that it's neither a header file nor a separate compilation unit.
The compilation unit "main.cpp" could look like this:
#include "tools.h"
#include "tools.inl"
main()
{
int number[] = {1,2,3};
getaverage(numbers);
}
Since the compiler identifies the required specialization it can generate the code from the implementation file.
For most cases, harper's answer is appropriate. But for completeness' sake, explicit template instantiation should also be mentioned.
When you include the implementation in every compilation unit, your template classes and functions will be instantiated and compiled in all of them. Sometimes, this is not desirable. It is mostly due to compile-time memory restrictions and compilation time, if your template classes and functions are very complicated. This becomes a very real issue when you, or the libraries you use rely heavily on template metaprogramming. Another situation could be that your template function implementations might be used in many compilation units, and when you change the implementation, you will be forced to re-compile all those compilation units.
So, the solution in these situations is to include a header file like your tools.h, and have a tools.cpp, implementing the templates. The catch is that, you should explicitly instantiate your templates for all the template arguments that will be used throughout your program. This is accomplished via adding the following to tools.cpp:
template double getAverage<int,85>(int (&numbers)[85]);
Note: You obviously have to do something about that "85", such as defining it in a header file and using it across tools.cpp and main.cpp
I've found this article which is useful : templates and header files
I declared the function in the Tools.h file and include there the file Tool.hpp and after this I defined them in the Tools.hpp file.
I haven't tried to compile .cpp and .c files together but maybe my example will help.
I had similar problem compiling two separate assembly files .s on mingw with standard gcc
compiler and i achieved it as follows:
gcc -m32 -o test test.s hello.s
-m32 means i'm compiling 32bit code
-o is the output file ( which in my example is the "test" file )
test.s and hello.s are my source files. test.s is the main file and hello.s has the helper function. (Oh, to mention is the fact that both files are in the same directory)

C++ Template Class Problem

Hi everybody I just wanted to practice some c++ template but i get linker errors. Can anybody help me please?
Here is my code:
// File: MyClass.h
#ifndef _MYCLASS_H
#define _MYCLASS_H
template<class T> class MyClass {
T value;
public:
MyClass(T v);
~MyClass();
};
#endif // _MYCLASS_H
// File: MyClass.cpp
#include "MyClass.h"
template<class T> MyClass<T>::MyClass(T v) {
value = v;
}
template<class T> MyClass<T>::~MyClass() {
}
// File: main.cpp
#include "MyClass.h"
int main() {
MyClass<int> test(10);
return 0;
}
Here is command line output:
g++ main.cpp -c
g++ MyClass.cpp -c
g++ main.o MyClass.o -o Out
main.o: In function `main':
main.cpp:(.text+0x1a): undefined reference to `MyClass<int>::MyClass(int)'
main.cpp:(.text+0x2b): undefined reference to `MyClass<int>::~MyClass()'
collect2: ld returned 1 exit status
make: *** [all] Error 1
As you can see I'm using Ubuntu 10.04 and GNU C++ Compiler.
Am I missing something in this code?
Thanks for replies. It works but isn't there a better way to protect the code?
For example what if I want to create a non-opensource library?!
I want to export the code to a static library. and link the library to other projects ...
You have to put full template into the header. Compiler needs to see the body of the template methods at the site of template instantiation - main.cpp in your case. See, for example, C++ FAQ.
You should put template classes and inline methods into header files. You can't seperate definition and implementation in their case.
#Nikolai N Fetissov has the right solution. I would add to this that a nice way to do this, if you want to keep the implementation and templated function definitions separate is that you can put the implementations into MyClass.hxx and include it at the end of your MyClass.h
// File: MyClass.h
#ifndef _MYCLASS_H
#define _MYCLASS_H
template<class T> class MyClass
{
T value;
public:
MyClass(T v);
~MyClass();
};
#include "MyClass.hxx" /// <--- like this
#endif // _MYCLASS_H
It's important to remember what a template is. It is a template for generating code if needed; it is not code itself.
So declaring a template class and writing implementations for those methods does not generate any object code for that class; it simply provides a template for doing so if necessary.
When a template class is instantiated with an actual argument, the compiler will generate the code from the template class. In order to do that, it needs to be able to see the the templates. But since you've only #includeed the .h file, and the implementation of the methods in in the .cpp file, the compiler won't be able to generate the object code for the function implementations. Then, when the linker looks for those definitions it won't find it.
All of this is a long-winded way of getting to the same result the other answers did -- you need to put the implementation in the header file with the class declaration. But it may help to know why that is.