Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'd like to provide a library that provides template code. But I would also like to keep the most possible the ownership of this code (generated code) when I can guess the usage of different usual types of my template. Here is an example of what I am trying to do:

lib1.h

#include <iostream>

template<int N>
void print_me() {
    std::cout << "I am function number " << N << std::endl;
}

lib1.cpp

#include "lib1.h"

/* Force symbols to be defined here. */
template void print_me<0>();
template void print_me<1>();

I compile my library using:

g++ -shared -fPIC lib1.cpp -o lib1.so

And when I use my library:

main.cpp

#include <lib1.h>

int main() {
    print_me<0>();
    print_me<1>();
    print_me<2>();
}

Compiled with:

g++ main.cpp -l1

Here I would expect that the symbol print_me<0>() and print_me<1>() are defined and used from lib1.so and print_me<2>() defined and used for my executable (checked with nm --defined-only). But it seems that this is not the case! The symbols for 0 and 1 are well defined in lib1.so but as weak symbols. And are redefined in my executable (0, 1 and 2) again, weak. That implies that the code for 0 and 1 for my executable is taken from main.cpp which is not what I want (I checked with specification in main.cpp).

Is there a way (in the lib1.h for instance) to say at compile time of main.cpp that the symbols are already defined somewhere and that it does not need to add these symbols?

like image 574
joetde Avatar asked Feb 13 '23 22:02

joetde


2 Answers

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.

like image 59
Constructor Avatar answered Apr 28 '23 12:04

Constructor


I would separate the implementation and the interface, using template specialization:

lib1.h:

#include <iostream>
template <int T> void print_me(void);
template <> void print_me<0>(void);
template <> void print_me<1>(void);

lib1_internal.h (NOTE: this does not need to be disclosed):

#include <iostream>

template<int N>
void print_me_internal() {
    std::cout << "I am function number " << N << std::endl;
}

lib1.cpp:

#include "lib1.h"
#include "lib1_internal.h"

template <> void print_me<0>() {
  print_me_internal<0>();
}
template <> void print_me<1>() {
  print_me_internal<1>();
}

your main.cpp will correctly lead to a linker error:

$ g++ main.cpp -L. -l1
/tmp/ccZSDqkp.o: In function `main':
main.cpp:(.text+0xf): undefined reference to `void print_me<2>()'
collect2: ld returned 1 exit status

Just add the definition of template <int T> void print_me(void) in lib1.h in place of its declaration and it will be used whenever not found in the specialized versions.

like image 29
Sigi Avatar answered Apr 28 '23 12:04

Sigi