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

ぐ巨炮叔叔 提交于 2020-05-23 14:50:06

问题


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?


回答1:


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.




回答2:


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.




回答3:


You have to hide the implementation in the header file.

//lib1.h    
template<int N>
void print_me();

//lib1.cpp
#include <iostream>
#include "lib1.h"
template <int printme>
void print_me()
{
  std::cout << printme << std::endl;
}

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

What is happening is how templates are typically used: they are only built when needed (else you'd have to build a print_me for all integers), so they figure out the implementation when the software runs (which is why it slows down compilation time so much, it rebuilds the class for every compilation unit that uses the template). As long as you have the definition of the template, you can build it for whatever argument you need. This is one of the few cases where hiding the definition in the .cpp file is semi valid, but the end user will typically get one hell of a useless error for print_me<2>(). It'd be better (imho) to use this framework (C++11):

//lib1.h
#include <type_traits>
template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me();

//lib1.cpp
#include <iostream>
#include "lib1.h"

template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me()
{
  std::cout << N << std::endl;
}

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

now the error is "no matching function for call to print_me()" which is a little better...you could do a nested function with a static_assert to make it even better.



来源:https://stackoverflow.com/questions/21481864/force-definition-of-symbol-for-a-c-template-instance-in-a-library

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!