Symbol Not Found When Using Template Defined in a Library

Symbol not found when using template defined in a library

Basically what's happened is that you only have the definitions in the headers
if I say

template<class T> T something(T); somewhere, that tells the compiler "trust me bro, it exists, leave it to the linker"

and it adds the symbol to the object file as if it did exist. Because it can see the prototype it knows how much stack space, what type it returns and such, so it just sets it up so the linker can just come along and put the address of the function in.

BUT in your case there is no address. You /MUST/ have the template definition (not just declaration) in the same file so the compiler can create one (with weak linkage) so here it's assumed they exist, but no where does it actually stamp out this class from a template, so the linker doesn't find it, hence the error.

Will fluff out my answer now, hope this helps.

Addendum 1:

template<class T> void output(T&);

int main(int,char**) {
int x = 5;
output(x);
return 0;
}

This will compile but NOT link.

Output:

if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings  -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/main.cpp -o build/main.o
g++ build/main.o -o a.out
build/main.o: In function `main':
(my home)/src/main.cpp:13: undefined reference to `void output<int>(int&)'
collect2: error: ld returned 1 exit status
make: *** [a.out] Error 1

(I hijacked an open projet for this hence the names)

As you can see the compile command works fine (the one that ends in -o build/main.o) because we tell it "look this function exists"

So in the object file it says to the linker (in some "name managled form" to keep the templates) "put the location in memory of void output(int&); here" the linker can't find it.

Compiles and links

#include <iostream>
template<class T> void output(T&);

int main(int,char**) {
int x = 5;
output(x);
return 0;
}

template<class T> void output(T& what) {
std::cout<<what<<"\n";
std::cout.flush();
}

Notice line 2, we tell it "there exists a function, a template in T called output, that returns nothing and takes a T reference", that means it can use it in the main function (remember when it's parsing the main function it hasn't seen the definition of output yet, it has just been told it exists), the linker then fixes that. 'though modern compilers are much much smarter (because we have more ram :) ) and rape the structure of your code, link-time-optimisation does this even more, but this is how it used to work, and how it can be considered to work these days.

Output:

make all 
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/main.cpp -o build/main.o
g++ build/main.o -o a.out

As you can see it compiled fine and linked fine.

Multiple files without include as proof of this

main.cpp

#include <iostream>

int TrustMeCompilerIExist();

int main(int,char**) {
std::cout<<TrustMeCompilerIExist();
std::cout.flush();
return 0;
}

proof.cpp

int TrustMeCompilerIExist() {
return 5;
}

Compile and link

make all 
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -MM src/main.cpp >> build/main.o.d ; then rm build/main.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/main.cpp -o build/main.o
if ! g++ -Isrc -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -MM src/proof.cpp >> build/proof.o.d ; then rm build/proof.o.d ; exit 1 ; fi
g++ -Wall -Wextra -O3 -std=c++11 -g -gdwarf-2 -Wno-write-strings -Isrc -c src/proof.cpp -o build/proof.o
g++ build/main.o build/proof.o -o a.out

(Outputs 5)

Remember #include LITERALLY dumps a file where it says "#include" (+ some other macros that adjust line numbers) this is called a translation unit. Rather than using a header file to contain "int TrustMeCompilerIExist();" which declares that the function exists (but the compiler again doesn't know where it is, the code inside of it, just that it exists) I repeated myself.

Lets look at proof.o

command

objdump proof.o -t

output

proof.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 proof.cpp
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 0000000000000006 _Z21TrustMeCompilerIExistv

Right at the bottom there, there's a function, at offset 6 into the file, with debugging information, (the g is global though) you can see it's called _Z (this is why _ is reserved for some things, I forget what exactly... but it's to do with this) and Z is "integer", 21 is the name length, and after the name, the v is "void" the return type.

The zeros at the start btw are the section number, remember binaries can be HUGE.

Disassembly
running:

objdump proof.o -S gives

proof.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <_Z21TrustMeCompilerIExistv>:

int TrustMeCompilerIExist() {
return 5;
}
0: b8 05 00 00 00 mov $0x5,%eax
5: c3 retq

Because I have -g you can see it put the code that the assembly relates to (it makes more sense with bigger functions, it shows you what the following instructions until the next code block actually do) that wouldn't normally be there.

main.o

Here's the symbol table, obtained the same way as the above:

objdump main.o -t

main.o: file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 main.cpp
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .text.startup 0000000000000000 .text.startup
0000000000000030 l F .text.startup 0000000000000026 _GLOBAL__sub_I_main
0000000000000000 l O .bss 0000000000000001 _ZStL8__ioinit
0000000000000000 l d .init_array 0000000000000000 .init_array
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_loc 0000000000000000 .debug_loc
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_ranges 0000000000000000 .debug_ranges
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text.startup 0000000000000026 main
0000000000000000 *UND* 0000000000000000 _Z21TrustMeCompilerIExistv
0000000000000000 *UND* 0000000000000000 _ZSt4cout
0000000000000000 *UND* 0000000000000000 _ZNSolsEi
0000000000000000 *UND* 0000000000000000 _ZNSo5flushEv
0000000000000000 *UND* 0000000000000000 _ZNSt8ios_base4InitC1Ev
0000000000000000 *UND* 0000000000000000 .hidden __dso_handle
0000000000000000 *UND* 0000000000000000 _ZNSt8ios_base4InitD1Ev
0000000000000000 *UND* 0000000000000000 __cxa_atexit

See how it says undefined, that's because it doesn't know where it is, it just knows it exists (along with the standard lib stuff, which the linker will find itself)

In closing
USE HEADER GUARDS and with templates put #include file.cpp at the bottom BEFORE the closing header guard. that way you can include header files as usual :)

Template Class – Symbols not found

Detailed Explanation available from http://www.parashift.com/c++-faq-lite/templates.html

[35.12] Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?

If all you want to know is how to fix this situation, read the next two FAQs. But in order to understand why things are the way they are, first accept these facts:

  1. A template is not a class or a function. A template is a "pattern" that the compiler uses to generate a family of classes or functions.
  2. In order for the compiler to generate the code, it must see both the template definition (not just declaration) and the specific types/whatever used to "fill in" the template. For example, if you're trying to use a Foo, the compiler must see both the Foo template and the fact that you're trying to make a specific Foo.
  3. Your compiler probably doesn't remember the details of one .cpp file while it is compiling another .cpp file. It could, but most do not and if you are reading this FAQ, it almost definitely does not. BTW this is called the "separate compilation model."

Now based on those facts, here's an example that shows why things are the way they are. Suppose you have a template Foo defined like this:

template<typename T>
class Foo {
public:
Foo();
void someMethod(T x);
private:
T x;
};

Along with similar definitions for the member functions:

 template<typename T>
Foo<T>::Foo()
{
...
}

template<typename T>
void Foo<T>::someMethod(T x)
{
...
}

Now suppose you have some code in file Bar.cpp that uses Foo:

// Bar.cpp

void blah_blah_blah()
{
...
Foo<int> f;
f.someMethod(5);
...
}

Clearly somebody somewhere is going to have to use the "pattern" for the constructor definition and for the someMethod() definition and instantiate those when T is actually int. But if you had put the definition of the constructor and someMethod() into file Foo.cpp, the compiler would see the template code when it compiled Foo.cpp and it would see Foo when it compiled Bar.cpp, but there would never be a time when it saw both the template code and Foo. So by rule #2 above, it could never generate the code for Foo::someMethod().

A note to the experts: I have obviously made several simplifications above. This was intentional so please don't complain too loudly. If you know the difference between a .cpp file and a compilation unit, the difference between a class template and a template class, and the fact that templates really aren't just glorified macros, then don't complain: this particular question/answer wasn't aimed at you to begin with. I simplified things so newbies would "get it," even if doing so offends some experts.

[35.13] How can I avoid linker errors with my template functions?

Tell your C++ compiler which instantiations to make while it is compiling your template function's .cpp file.

As an example, consider the header file foo.h which contains the following template function declaration:

// File "foo.h"

 template<typename T>
extern void foo();

Now suppose file foo.cpp actually defines that template function:

// File "foo.cpp"

 #include <iostream>
#include "foo.h"

template<typename T>
void foo()
{
std::cout << "Here I am!\n";
}

Suppose file main.cpp uses this template function by calling foo():

// File "main.cpp"

 #include "foo.h"

int main()
{
foo<int>();
...
}

If you compile and (try to) link these two .cpp files, most compilers will generate linker errors. There are three solutions for this. The first solution is to physically move the definition of the template function into the .h file, even if it is not an inline function. This solution may (or may not!) cause significant code bloat, meaning your executable size may increase dramatically (or, if your compiler is smart enough, may not; try it and see).

The other solution is to leave the definition of the template function in the .cpp file and simply add the line template void foo(); to that file:

// File "foo.cpp"

 #include <iostream>
#include "foo.h"

template<typename T> void foo()
{
std::cout << "Here I am!\n";
}

template void foo<int>();

If you can't modify foo.cpp, simply create a new .cpp file such as foo-impl.cpp as follows:

// File "foo-impl.cpp"

 #include "foo.cpp"

template void foo<int>();

Notice that foo-impl.cpp #includes a .cpp file, not a .h file.

C++ Shared Library with Templates: Undefined symbols error

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 inheritance: Symbol not found

I could reproduce the linker error after adding an int main() { } and compiling with g++ without additional options:

sliding_sd.cpp:(.rdata$_ZTV6SliderIdE[__ZTV6SliderIdE]+0x8): undefined reference to `Slider<double>::save_result()'

This means there is a reference to the virtual method Slider<double>::save_result() in the vtable (virtual method table) of Slider<double> which could not be resolved during linking. The cause is that you declared save_result() in template <class T> class Slider but did not define it (at least in the code you have posted). Albeit Slider<double>::save_result() is never used in your example, it must be defined (at least when using ordinary C++, I do not know about R), as it is virtual. Otherwise the vtable for Slider<double> cannot be created during linking.

When changed to

template <class T>
class Slider {
/* ... */
virtual void save_result() { }
};

the linking error disappears (at least in my reproduction).

Why do I get missing symbols for an explicit template specialization in a static library?

You have member function definitions within class (template) definitions. This causes the member functions (templates) to be inline. That doesn't matter so much for the member function of the template class, since its linkage requirements are determined more by the nature of its instantiation(s).

But in the second example, the member function void TemplatedClass<long>::Test(long) is not a function template and is still inline. So the compiler is not required to do anything with it unless it's used, and it must be defined in all files where it's used. Since you claim this is in a static.cpp file, an inline function is probably not what you want.

I think you'll get results more like you're expecting if you change things to:

template <>
struct TemplatedClass < long >
{
void Test( long value );
};

void TemplatedClass<long>::Test( long value )
{
std::cout << "Value was: " << value << std::endl;
}

And when you define an explicit specialization, you probably don't also need an explicit instantiation (if that's even legal).

Undefined symbols linker error with simple template class

The template definition (the cpp file in your code) has to be included prior to instantiating a given template class, so you either have to include function definitions in the header, or #include the cpp file prior to using the class (or do explicit instantiations if you have a limited number of them).

Force definition of symbol for a C++ template instance in a library

C++11 solution: use extern templates. Simply add these strings to your main.cpp file:

extern template void print_me<0>();
extern template void print_me<1>();

Thus you tell compiler not to instantiate print_me function template in main.cpp (for template arguments 0 and 1). So linker should search definition of void print_me<0>(); and void print_me<1>(); in other translation units.



Related Topics



Leave a reply



Submit