延迟C++静态对象构建-Linux上的GCC

Deferring C++ static object construction - GCC on Linux

本文关键字:-Linux 上的 GCC 构建 对象 C++ 静态 延迟      更新时间:2023-10-16

假设我有一个名为MyClass的C++类。

想象一下,我无法访问MyClass的源代码。。。它包含在库中,并且仅向我提供用于CCD_ 3的库和头文件。

想象一下,类本身需要环境预配置。。。例如在调用类的构造函数之前,我需要进行一些设置。该类通常用于以下用途:

void func() {
   doGlobalSetup();
   MyClass myInstance(1,2,3);
   myInstance.doSomething();
   ...
}

现在我有这样的情况,我们需要创建类的全局实例,例如:

MyClass myInstance(1,2,3);
int main(int argc, char *argv[]) {
   doGlobalSetup();
   myInstance.doSomething();
}

问题是,在这个故事中,MyClass的实例是在调用doGlobalSetup()之前创建的。它在调用main()之前被实例化。我想做的是将myInstance()的创建推迟到稍后,或者能够在类实例化之前以某种方式运行doGlobalSetup()

这是对实际故事的简化。。。所以让我们假设:

  1. 我无法更改MyClass的内部结构
  2. 必须有一个MyClass类型的名为myInstance的实例变量(我不能将逻辑更改为MyClass *pMyInstance

非常感谢您的阅读。

由于您已经将问题约束为无法使用new,因此您应该能够一如既往地创建对象并将其复制到全局实例。例如:

MyClass createMyClass()
{
    doGlobalSetup();
    return MyClass(1, 2, 3);
}
MyClass myInstance = createMyClass();
int main()
{
    myInstance.doSomething();
    return 0;
}

它适合您的需求吗?

namespace
{
    int doStaticGlobalSetup()
    {
        doGlobalSetup();
        return 0;
    }
}
MyClass myInstance(doStaticGlobalSetup() + 1,2,3);
int main() {
   myInstance.doSomething();
   return 0;
}

如果您必须将任何构造函数调用推迟到全局初始化完成之后,并且希望确保不会发生静态顺序初始化失败,那么有一种方法:使myInstance引用未初始化的内存块,并在全局初始化后使用placement new在其中创建对象。

#include <iostream>
#include <type_traits>
struct foo
{
    foo() { std::cout << "createdn"; }
    void meow() { std::cout << "usedn"; }
    ~foo() { std::cout << "destroyedn"; }
};
void doGlobalSetup() { std::cout << "Global setupn"; }

//Actual implementation
namespace {
    typename std::aligned_storage<sizeof(foo), alignof(foo)>::type bar;
}
foo& instance = reinterpret_cast<foo&>(bar);
//Allows automatic creation and destruction
struct initializer
{
    initializer()
    {
        if (!initialized)
            new (&instance) foo();
        initialized = true;
    }
    ~initializer()
    {
        if(initialized)
            instance.~foo();
        initialized = false;
    }
    private:
        static bool initialized;
};
bool initializer::initialized = false;
int main()
{
    doGlobalSetup();
    initializer _;
    instance.meow();
}

在函数中使用静态变量。

MyClass &myInstance() {
   doGlobalSetup();
   static MyClass myInstance(1,2,3);
   return myInstance;
}
void func() {
   myInstance().doSomething();
}

您可能已经得到了想要的答案。但为了覆盖整个范围:如果出于某种原因,您想确保代码中的其他地方不会意外地独立于全局变量构建MyClass,并且在进行全局设置之前,您需要通过链接来解决这个问题。

如果您在Linux上,您可以LD_PRELOAD一个共享对象,该对象只包含MyClass构造函数的符号。在它中,您相应地声明设置函数,并让动态链接器为您完成任务。然后,在构造函数内部,调用setup函数,然后执行dlsym("...", RTLD_NEXT)以获得指向原始构造函数的指针,然后调用它,将获得的参数传递给它。当然,您需要维护并检查一个静态标志,以确定是否已经执行了设置。

同样,这对你来说可能有些过头了,但我发布它只是为了以防万一有人需要(并且能够使用)这种解决方案。

附言:这就是当你依赖全球状态时所得到的!:)

首先,请记住,给定像doGlobalSetup这样的库初始化函数,如果创建全局实例,则库将不起作用的可能性明显为非零。

否则,使用逗号运算符创建初始值设定项是非常容易的:

bool do_my_setup = (doGlobalSetup(), true);
MyClass myInstance(1,2,3);

GCC编译器环境中有一个名为constructor的函数属性功能。这使我们能够标记函数定义,使其能够在调用main之前自动调用,最重要的是在调用任何类构造函数之前自动调用

回到最初的问题定义。。。如果doGlobalSetup()函数修改自:

void doGlobalSetup() { ... }

__attribute__((constructor)) void doGlobalSetup() { ... }

那么它的调用将发生在main被调用之前,也将发生在任何静态类实例构造函数被调用之前。对该函数的显式调用也将从main()中删除,因为它的工作是隐式执行的。