【问题标题】:Prevent linker from removing globals防止链接器删除全局变量
【发布时间】:2009-04-30 10:44:25
【问题描述】:

我使用静态全局变量构造函数来方便地注册函数,想法是这样的:

typedef int (*FuncPtr)(int);

struct RegHelper
{
    RegHelper(const char * Name, FuncPtr Func)
    {
        Register(Name, Func);
    }
}

#define REGISTER(func) RegHelper gRegHelper_ ## func (#func, func);

现在我可以通过这种方式注册函数(我用它来实现某种反射):

int Foo(int a)
{
    return a * 123;
}

REGISTER(Foo)

int Bar(int a)
{
    return a * 456;
}

REGISTER(Bar)

问题是如果我在静态库中使用它,有时链接器会检测到编译单元没有被使用,它会丢弃整个东西。所以没有构造全局变量,也没有注册函数……

我的问题是:我可以做些什么来解决这个问题?在初始化期间在每个编译单元中调用虚拟函数似乎会触发全局变量的构造,但这感觉不是很安全。还有什么建议吗?

【问题讨论】:

    标签: c++ linker global-variables


    【解决方案1】:

    解决这个问题:

    • Visual Studio(在同一解决方案中):链接器 > 常规 > 使用库依赖输入 = 是
    • Gcc : 直接与 .o 文件链接

    我还没有找到我真正喜欢的解决方案。

    【讨论】:

    • 谢谢!这不完全是我正在寻找的解决方案,但这已经很有帮助:)
    • 我遇到了同样的问题,但打开“使用库依赖输入”对我不起作用。我的静态库被链接到一个 Dll 中,如果这有什么不同的话。
    • 我添加了一个新答案,因为我的工作方式不同。
    【解决方案2】:

    在此处查看答案:force visual studio to link all symbols in a lib file

    这个确实对我有用。其他建议没有。

    【讨论】:

      【解决方案3】:

      是的,我也遇到过这个问题。我发现的唯一可靠的方法是:

      • 将库制作成 DLL

      或:

      • 将注册对象移动到可执行文件中

      这些都不是完美的,虽然如果你不介意使用 DLLS,DLL 解决方案是可以的,我也很想听听其他解决方案。

      【讨论】:

        【解决方案4】:

        查看"Best Way To Build a List of Per Type Data"的答案

        其中有两个关键的重要概念。第一:

        (void) register_object;  
        

        使用对象来确保链接器不会剥离它,并且,

        template<typename D> typename automatic_register<D>::exec_register 
            automatic_register<D>::register_object;
        

        确保为每个实例分配一个静态全局变量。您应该将数据保存在此对象中。它有点不同,因为它是每个对象而不是每个类,但是如果你调整你的宏来创建

        // Global list of objectsh
        struct FpList
        {
           FpList( FuncPtr func ) :
              func(func)
           {
              head = next;
              next = this
           }
           FpList* next;
           FuncPtr func;
        
           static FpList* head;
        };
        // In .cxx:
        FpList* FpList::head = 0;  
        

        然后修改您注册的宏,以便 REGISTER( Foo ),以便它创建:

        struct register_Foo : FpList
        {
           register_Foo( FuncPtr fn ): FpList(fn)
           {
              (void) register_object;  
           }
           static register_Foo register_object;
        };
        

        我认为这还不够。您仍然需要实例化模板,传递 if &Foo 并确保 a

        register_Foo register_Foo::register_object
        

        实例是在某处创建的。 automatic_register 的模板代码显示了如何在标题中执行此操作。如果您可以将宏放在 .cxx 中,只需声明:

        register_Foo register_Foo::register_object( &Foo );
        

        作为宏的一部分。我认为它可能会奏效。 (全凭记忆,谁知道呢)。

        【讨论】:

          【解决方案5】:

          如果您在 UNIX 环境中调用带有 whole-archive 选项的 ld 将强制所有对象文件包含在静态库中,无论使用如何。

          【讨论】:

            【解决方案6】:

            同一问题的另一种可能的解决方案。通过函数调用获取常量值。像这样:

            常数.h

            const char blah[10];
            
            extern const char *get_blah();
            

            常数.c

            #include "header.h"
            
            const char *get_blah()
            { return blah; }
            

            这帮助我成功了!

            【讨论】:

            • 为什么会这样:当编译器看到 constant.h 时,它需要函数 get_blah 使用 constant.h 链接到翻译单元。由于函数在constant.c中,所以使用整个constant.c.o而不是像以前那样被丢弃。
            • get_blah 这个名字在这里没有任何影响,重要的是extern 技巧
            猜你喜欢
            • 1970-01-01
            • 2011-09-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多