在静态库中的主呼叫功能



我有一个类型的注册系统,用于自定义的运行时类型信息。到现在为止

#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

当您拥有静态库时,问题是,默认情况下,只有所需的符号将链接到您的可执行文件或库中。根据定义,这些不会是您的寄存器静态对象。

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

在Windows上,使用/WHOLEARCHIVE

另一个选项是从您的可执行/共享库中的某个地方引用这些对象,以强制其集成。

所以两个选项:

  • 整个存档,简单,但整合了静态库中的所有内容
  • 手动引用 main中的寄存器函数(只有object;就足够了)

我最终在名为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 Filesomes中,我以前使用寄存器宏:

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

如果您制作一个列出所有REGISTER_EXTERN调用的标题文件,则可以实际指定将运行寄存器功能的顺序,因为静态时第一次命中,它将初始化并调用该功能。助手确保仅调用REGISTER功能一次,因为在不同位置包含标头将重新定位每个翻译单元中的静态变量。

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

最新更新