在静态库中的main之前调用函数

时间:2018-12-19 20:30:25

标签: c++ visual-c++ static-libraries static-initialization

我有一个类型注册系统,用于自定义形式的运行时类型信息。到目前为止,我已经使用以下宏在main之前调用注册函数并注册类型:

#define REGISTRATION                                    \
static void _register();                                \
namespace { struct temp { temp() { _register(); } }; }  \
static const temp CAT(temp, __LINE__);                  \
static void _register()

这样,我可以在许多不同的cpp文件中做到这一点:

REGISTRATION()
{
    RegisterNewType(vec2)
    ->RegisterMember("x", &vec2::x)
    ->RegisterMember("y", &vec2::y);
}

这很好。但是,当我尝试在静态库中执行此操作时,这会分崩离析。看来这是因为C ++不会在静态库中初始化静态变量。

我去搜索了一个解决方案,发现了这一点:

#define REGISTRATION                                  \
static void _register() __attribute__((constructor)); \
static void _register()

也许可以解决我的问题,但事实证明,我只能在clang和gcc中做到这一点。我正在使用msvc。

有没有一种方法可以使它正常工作,这样我就可以在库中拥有主要功能了?

编辑:更多的谷歌搜索使我想到了这一点:

#if defined(_MSC_VER)
#pragma section(".CRT$XCU",read)
#define EXT_REGISTRATION_(f,p) \
        static void __cdecl f(void); \
        __declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \
        __pragma(comment(linker,"/include:" p #f "_")) \
        static void __cdecl f(void)
#ifdef _WIN64
#define EXT_REGISTRATION(f) EXT_REGISTRATION_(f,"")
#else
#define EXT_REGISTRATION(f) EXT_REGISTRATION_(f,"_")
#endif
#else
#define EXT_REGISTRATION(f) \
        static void f(void) __attribute__((constructor)); \
        static void f(void)
#endif

但是仍然没有调用注册功能。调试器指出No symbols have been loaded

2 个答案:

答案 0 :(得分:2)

由于您有一个静态库,问题在于默认情况下,只有所需的符号才会链接到您的可执行文件或库中。根据定义,这些将不是您注册的静态对象。

在Linux上,使用--whole-archive告诉gcc(或clang)将完整的库集成到最终输出文件中。

在Windows上,使用/WHOLEARCHIVE

另一种选择是从可执行文件/共享库中的某处引用这些对象以强制其集成。

有两个选择:

  • 整个存档,简单,但集成了静态库中的所有内容
  • 手动引用main中的寄存器功能(只要拥有object;就足够了)

答案 1 :(得分:1)

我最终在一个名为Ethereal的游戏引擎中找到了解决方案。此代码归功于那个人。

我没有两个寄存器功能,而是两个:

#define CAT_IMPL(a, b) a##b
#define CAT(a, b) CAT_IMPL(a, b)

#define REGISTER_EXTERN(cls)\
    template <typename T>\
    extern void register_func();\
    template <>\
    void register_func<cls>();\
    static const int CAT(temp, __LINE__) =\
        reg_helper::call<cls>(&register_func<cls>)

#define REGISTER(cls)\
    template <> \
    void register_func<cls>()

然后我有这个辅助功能:

namespace reg_helper
{
    template <typename T>
    inline int call(void(*f)())
    {
        static const int s = [&f]() {
            f();
            return 0;
        }();
        return s;
    }
}

在标头函数中,我定义REGISTER_EXTERN(SomeClass);,然后在cpp文件中的某个地方像以前一样使用register宏:

REGISTER(SomeClass)
{
    // This code will run before main
}

如果您创建一个列出所有REGISTER_EXTERN调用的头文件,则实际上可以指定运行寄存器功能的顺序,因为自从第一次点击静态代码以来,它将初始化并调用功能。辅助程序确保REGISTER函数仅被调用一次,因为在不同位置包含标头将重新初始化每个翻译单元中的静态变量。

这解决了静态库的问题,因为将包含标头并且符号将存在。其次,它确保我可以按正确的顺序初始化事物。