函数模板的部分属性在 GCC 中被静默忽略

Posted

技术标签:

【中文标题】函数模板的部分属性在 GCC 中被静默忽略【英文标题】:section attribute of a function template is silently ignored in GCC 【发布时间】:2016-03-29 08:30:05 【问题描述】:

我正在尝试将一组特定的函数放在一个单独的部分中,但在使用 GCC 时遇到了麻烦。

namespace /* anonymous */ 
  [[gnu::section(".mysection")]]
  void regular_func()  

  template <class T>
  [[gnu::section(".mysection")]]
  void template_func()  
 // namespace /* anonymous */

void (*ptr1)() = &regular_func;
void (*ptr2)() = &template_func<int>;

使用 clang,regular_functemplate_func&lt;int&gt; 的符号都按我的预期放置在 .mysection 中。

$ clang++ -std=c++14 a.cpp -c && objdump -t a.o | grep -E "regular|template"
0000000000000000 l     F .mysection 0000000000000006 _ZN12_GLOBAL__N_112regular_funcEv
0000000000000010 l     F .mysection 0000000000000006 _ZN12_GLOBAL__N_113template_funcIiEEvv

但是使用GCC,函数模板不是放在.mysection,而是放在.text.*部分。

$ g++ -std=c++14 a.cpp -c && objdump -t a.o | grep -E "regular|template"
0000000000000000 l     F .mysection 0000000000000007 _ZN12_GLOBAL__N_112regular_funcEv
0000000000000000 l     F .text  0000000000000007 _ZN12_GLOBAL__N_113template_funcIiEEvv

我正在使用 clang-3.7.1 和 gcc-5.3.0。

如何强制 gcc 将模板实例化的函数放在单独的部分?

【问题讨论】:

提交了一个错误:gcc.gnu.org/bugzilla/show_bug.cgi?id=70435 【参考方案1】:

这可能是一个小小的安慰,但如果您申请 section,GCC 将有义务 归因于template &lt;class T&gt; void template_func() 的显式实例化, 对于您想要实例化的每个T,例如

namespace /* anonymous */ 
    [[gnu::section(".mysection")]]
    void regular_func()  

    template <class T>
    void template_func()  

    template [[gnu::section(".mysection")]] void template_func<int>();

 // namespace /* anonymous */


void (*ptr1)() = &regular_func;
void (*ptr2)() = &template_func<int>;

然后:

$ g++ -std=c++14 a.cpp -c && objdump -C -t a.o | grep -E "regular|template"
0000000000000000 l     F .mysection 0000000000000007 (anonymous namespace)::regular_func()
0000000000000007 l     F .mysection 0000000000000007 void (anonymous namespace)::template_func<int>()

不幸的是 clang 拒绝:

template [[gnu::section(".mysection")]] void template_func<int>();

说:

template [[gnu::section(".mysection")]] void template_func<int>();
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: an attribute list cannot appear here

所以每个编译器都必须有自己的方式,通过条件编译。

此外,此修复程序带来了额外的麻烦,您必须以某种方式确保 template_func() 无法为您未明确显示的任何 T 实例化 实例化。

您可以通过在函数模板的主体中静态断言 T 是您允许实例化的 A,B,C... 类型之一。那么如果它 曾经用T = D 实例化,static_assert 将触发;你可以 将D 添加到列表中并为D 添加显式实例化:

#include <type_traits>

template<typename T, typename First>
constexpr bool is_in()

    return std::is_same<T,First>::value;


template<typename T, typename First, typename Second, typename ...Rest>
constexpr bool is_in()

    return is_in<T,First>() || is_in<T,Second,Rest...>();


namespace /* anonymous */ 
    [[gnu::section(".mysection")]]
    void regular_func()  

    template <class T>
    void template_func() 
    
        static_assert(is_in<T,int,float>(),"");
    

    template [[gnu::section(".mysection")]] void template_func<int>();
    template [[gnu::section(".mysection")]] void template_func<float>();

 // namespace /* anonymous */


void (*ptr1)() = &regular_func;
void (*ptr2)() = &template_func<int>;
void (*ptr3)() = &template_func<float>;
void (*ptr4)() = &template_func<char>; // <-- static_assert fails

【讨论】:

有趣的是,该属性可以用于显式实例化,但不幸的是,这不是我的选择。我在示例中将参数简化为单个类型 T 但它实际上是可变参数,因此我无法手动实例化所有可能的组合。【参考方案2】:

不是最优雅的做事方式,但您可以进行一些名称修改并修改链接器脚本以获得所需的行为

#define TEMPLATE_SECTION(name, section) name##_##section##_

#define template_func TEMPLATE_SECTION(template_func, mysection)

template<class T>
void template_func()  

void regular_func()  

int main() 
    regular_func();         //placed in ".text"
    template_func<int>();   //placed in ".mysection"
    template_func<float>(); //placed in ".mysection"
    return 0;


在链接描述文件中

SECTIONS 
    .mysection : 
        *(*_mysection_*) 
    

    .text : 
        *(.text .text.*)
    

【讨论】:

以上是关于函数模板的部分属性在 GCC 中被静默忽略的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能从 gcc 中的前身模板化成员函数访问祖先方法?

让gcc支持成员函数模板的trick

GCC C++14/17 成员函数指针模板参数的区别

gcc 升级导致模板函数的静态局部变量变得未知

没有前向声明的嵌套函数模板实例化可以在 GCC 上编译,但不能在 clang 上编译

gcc 的 C++ 部分模板专业化问题