内联模板功能的可见性

时间:2018-09-06 11:50:49

标签: c++ templates gcc visibility elf

编译这一事实背后的原因是什么

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告诉编译器隐藏它们-或至少隐藏它们的实例化,希望它们也可以内联。

显式隐藏功能模板func1func3,即使用

 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:
        *;
};

据我所知,这只有在我不使用任何异常处理或不跨库边界进行动态类型转换的情况下才切实可行。但是那些内联函数会发生什么呢?感觉好像整个隐藏的可见性都违反了一个定义规则,因此没什么大不了的。

2 个答案:

答案 0 :(得分:0)

答案前有两个方面:

1)如果要在模块外部使用内联或模板函数,则应将它们放入.h {pp}文件中-对于模板,将在使用它们的每个模块上对其进行定义;

2)如果仅在当前编译单元上使用已定义的模板函数,则可以将它们声明为静态-无需声明静态内联函数,除非您使用函数指针来引用它们。

您说您不希望编译器为您生成的额外信息表明您不需要调试信息。使用-Os,-O2或-O3标志进行编译,您应该可以。

答案 1 :(得分:0)

GCC 不尊重 -fvisibility-inlines-hidden 的模板函数;这是一个bug that was fixed starting from gcc-10

在 GCC 10 中,所有四个函数都具有隐藏的可见性(命令行标志优先于在封闭命名空间上指定的可见性属性)。