2014-01-31 37 views
4

我想提供一个提供模板代码的库。但是,当我能够猜测不同类型的模板的使用情况时,我也想尽可能保留此代码的所有权(生成的代码)。这里是什么,我试图做一个例子:为库中的C++模板实例强制定义符号

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>(); 

我编译使用我的图书馆:

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

而当我使用我的图书馆:

main.cpp中

#include <lib1.h> 

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

编译时:

g++ main.cpp -l1

在这里,我会期望的是,符号print_me < 0>()和print_me < 1>()被定义和使用from lib1.so和print_me < 2>()定义并用于我的可执行文件(使用nm --defined-only进行检查)。但似乎并非如此! 0和1的符号在lib1.so中有明确的定义,但是符号如符号。并且在我的可执行文件(0,1和2)中重新定义为弱。这意味着我的可执行文件的代码为0和1取自main.cpp,这不是我想要的(我使用main.cpp中的规范进行了检查)。

有没有一种方法(例如在lib1.h中)在main.cpp的编译时说这些符号已经在某处定义了,并且它不需要添加这些符号?

+0

为什么你需要这样的东西?您的图书馆和用户都共享相同的模板(和专业化!)。定义模板意味着编译渴望而不是deamnd(懒惰)。虽然你的lib和你的用户代码的print_me <0>完全一样,但是它们生成的模板是一样的。 – Paranaix

+0

是否声明'template <> void print_me <0>(); template <> void print_me <1>();'在标题帮助? – Jarod42

+0

是否可以使用C++ 11功能? – Constructor

回答

3

C++ 11解决方案:使用extern templates。只要这些字符串添加到您的main.cpp文件:

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

因此,你告诉编译器不实例化print_me函数模板,在main.cpp(为模板参数0和1)。因此,链接器应该在其他翻译单元中搜索void print_me<0>();void print_me<1>();的定义。

1

您必须隐藏头文件中的实现。

//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>(); 

正在发生的事情是如何模板通常用于:需要(否则你就必须建立所有整数一个print_me)时,他们只建,所以他们想出的实施当软件运行(这这就是为什么它会减慢编译时间,所以它会重新编译每个使用该模板的编译单元的类)。只要你有模板的定义,你可以为你需要的任何参数构建它。这是将.cpp文件中的定义隐藏为半有效的少数情况之一,但对于print_me < 2>(),最终用户通常会得到一个无用的错误。它会更好(恕我直言)使用这个框架(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>(); 

现在的错误是这是一个好一点“的号召print_me()不匹配功能” ......你可以使用static_assert做一个嵌套函数,使其更好。

+0

正如Sigismondo的文章中所描述的那样,我仍然可以使用我没有预料到的病例模板(如本例中的2)。它不应该引发编译错误。 – joetde

3

我会分离的实施和接口,采用模板专业化:

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(注:这并不需要披露):

#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>(); 
} 

你的main.cpp将正确导致链接错误:

$ 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 

只需在lib1.h中添加template <int T> void print_me(void)的定义来替代其声明,并且只要在专用版本中找不到该定义即可使用它。

+0

专业化在这里并不是真的需要...事实上,你需要以同样的方式反复专门化功能,困扰我......我开始走下去,然后想“如果我只是想要0到100之间的所有值? ”。在你的解决方案中,我们需要100个专门化,这比100个初始化更令人讨厌,值得避免。 – IdeaHat

+0

OP询问了一种能够应对他可以猜测使用情况的类型的方法。他明确要求将它们编译并存储在.so库中,所以我认为这个解决方案解决了他的问题。 @Guillaume,你对此有何看法? – Sigismondo

+1

@Sigismondo是的,你的解决方案工作正常,做我想做的。这有点太糟糕了,我需要创建另一个头,以便能够做到这一点:(我在这个解决方案中看到两个缺点:首先,我需要有两个主体:一个在内部头和一个在外部头其次,用户仍然可以将模板专门化为0或1,然后“覆盖”我的代码 – joetde