【发布时间】:2019-02-11 16:21:55
【问题描述】:
编译的原因是什么
namespace ns __attribute__((visibility("default"))) {
template<typename T>
inline int func1(const T& x) {
return x;
}
inline int func2(int x) {
return x;
}
struct test {
template<typename T>
int func3(const T &x) { return x; }
int func4(int x) { return x; }
};
}
int __attribute__((visibility("default"))) client(int x) {
ns::test t;
const int y1 = ns::func1(x);
const int y2 = ns::func2(x);
const int y3 = t.func3(x);
const int y4 = t.func4(x);
return y1 + y2 + y3 + y4;
}
与
g++ -Wall -fPIC \
-fvisibility=hidden -fvisibility-inlines-hidden \
-shared -o libtest.so test.cpp
产生一个库导出ns::test::func1<int>() 和ns::test::func3<int>() 但不是ns::func2() 也不是ns::test::func4()?两个模板函数都定义了inline 和-fvisibility-inlines-hidden 告诉编译器隐藏它们——或者至少它们的实例化也希望是内联的。
显式隐藏函数模板func1 和func3,即使用
template<typename T>
int __attribute__((visibility("hidden"))) func(const T &x) { return x; }
导致预期的行为。省略命名空间定义中的默认可见性会隐藏两个实例化。
背景:我们尽量减少库中可见符号的数量。因此我们使用了上面提到的编译器标志和属性。当然,这对于所有静态第三方库也是必要的。但不幸的是,包含头文件中的那些内联模板函数完全不受我们控制。例如,每个实例化
namespace std {
template<typename _CharT, typename _Traits, typename _Alloc>
inline bool
operator==(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
const _CharT* __rhs)
{ return __lhs.compare(__rhs) == 0; }
}
来自#include <string> 将很高兴在我的库中生成一个公开可见的符号。而最烦人的部分,它会被放入我的库中,没有任何版本信息,所以没有GLIBCXX_3.x.z 来区分。
额外问题:使用链接器脚本的总体影响是什么
{
global:
_Z6clienti;
local:
*;
};
据我所知,这只有在我不使用任何异常处理或跨库边界的动态类型转换时才真正可行。但是这些内联函数会发生什么?感觉这整个隐藏的可见性无论如何都违反了一个定义规则,因此这没什么大不了的。
【问题讨论】:
-
“default”这个名字在某种程度上是错误的,至少是违反直觉的。它实际上意味着“公共”。整体编译器选项
-fvisibility=hidden或-fvisibility-inlines-hidden不会覆盖函数属性。在您的代码中,命名空间ns中的所有函数都被标记为具有 public 可见性。并且编译器因此可以导出内联函数的名称。见gcc.gnu.org/onlinedocs/gcc/… -
@Oliv:但这并不能解释内联函数和内联模板函数的实例化之间的区别。
-
确实,如果那个人 [编译器] 有任何错误,他会的。 -Edward A. Murphy
-
为了确保我们相互理解,通过声明公开可见的内联函数,您可以让编译器做出选择:要么生成函数代码并导出函数名称,要么不生成函数代码并且不导出函数名称。你比 Murphy 幸运,因为 Murphy 的技术人员会导出四个函数名!
标签: c++ templates gcc visibility elf