GCC __attribute__((constructor)) 在对象构造函数之前调用

Posted

技术标签:

【中文标题】GCC __attribute__((constructor)) 在对象构造函数之前调用【英文标题】:GCC __attribute__((constructor)) is called before object constructor 【发布时间】:2018-06-05 08:27:20 【问题描述】:

在我的共享库中,我需要将一些数据加载到 unordered_map 中,并尝试在标记为 __attribute__((constructor)) 的函数中执行此操作。但是,我在每次地图操作中都获得了 SIGFPE。在查看了***之后,我发现这意味着 unordered_map 未初始化。这对我来说是非常出乎意料和无法理解的,因为乍一看,它违反了 C++ 合同。任何人都可以帮助我在构造函数运行后如何运行这个方法?这是我自己的构造函数的一个工作示例,它表明它没有被调用:

#include <stdio.h>

class Ala 
    int i;
public:
    Ala() 
        printf("constructor called\n");
        i = 3;
    

    int getI() 
        return i;
    
;

Ala a;

__attribute__((constructor))
static void initialize_shared_library() 
    printf("initializing shared library\n");
    printf("a.i=%d\n", a.getI());
    printf("end of initialization of the shared library\n");

结果是

initializing shared library
a.i=0
end of initialization of the shared library
constructor called

但是,如果尝试使用 std::cout 而不是 printfs,那么它会立即进入 SEGFAULTs(因为流的构造函数没有运行)

【问题讨论】:

您可以尝试使用__attribute__((init_priority(123)) 语法,并为a 提供比此函数小的数字 您需要指定优先级以确保构造函数的正确顺序。最好远离编译器扩展,并按照答案中的建议使用 vanilla c++ 构造。 “它违反了 C++ 合同”:当然,它违反了 C++ 合同;当然您使用了编译器扩展——这意味着它不会像标准 C++ 那样运行。您需要阅读扩展的文档以了解其行为方式。 @MartinBonner 不幸的是,文档似乎很老套,它不包括诸如与没有该属性的全局对象的交互等内容 我认为这里发生的事情的顺序是不确定的。它当然似乎没有记录在案。这是一个旧线程,但请参阅:***.com/questions/8433484/… 【参考方案1】:

__attribute__((constructor)) 是编译器扩展,因此您离开了标准 C++ 领域。看起来 GCC 的构造函数是在全局初始化之前运行的。

修复它的方法是使用另一个 vanilla C++ 构造,例如一个全局对象,其初始化通过在与其他全局相同的 TU 中定义它来正确排序:

Ala a;

static void initialize_shared_library() 
    printf("initializing shared library\n");
    printf("a.i=%d\n", a.getI());
    printf("end of initialization of the shared library\n");


static int const do_init = (initialize_shared_library(), 0);

【讨论】:

可以将其表述为static struct init init() /* do init */ force_init; - 这避免了有点棘手的逗号运算符。【参考方案2】:

如果您使用返回静态局部变量的函数,该变量将在函数第一次被调用时被初始化。 见:When do function-level static variables get allocated/initialized?

struct Ala 
   // Your code here
   static Ala& get_singleton();
;

Ala& Ala::get_singleton() 
   static Ala singleton;
   return singleton;


static void initialize_shared_library() 
    Ala& a = get_singleton();
    printf("initializing shared library\n");
    printf("a.i=%d\n", a.getI());
    printf("end of initialization of the shared library\n");

正如其他人所说,通常最好将所有初始化过程封装在一个对象中,让构造函数为你完成工作(构造函数的主体在类的所有成员都初始化后执行)。

【讨论】:

【参考方案3】:

如果您只想在 a 初始化后运行某些东西,有几种方法可以在不离开标准 C++ 的情况下执行此操作;这是一个:

struct Ala2 : Ala

    Ala2()
    
         // your init code here
    
;

Ala2 a;

【讨论】:

以上是关于GCC __attribute__((constructor)) 在对象构造函数之前调用的主要内容,如果未能解决你的问题,请参考以下文章

C 语言编程 — GCC Attribute 语法扩展

C 语言编程 — GCC Attribute 语法扩展

等效于 Visual C++ 中 gcc 的 __attribute__ 格式

GCC __attribute__((constructor)) 在对象构造函数之前调用

GCC 的 __attribute__((__packed__)) 是不是保留原始顺序?

Linux gcc支持的语法 __attribute__ 属性设置