在静态库中调用 main 之前的函数
Calling function before main in static libraries
我有一个类型注册系统,用于自定义形式的运行时类型信息。到目前为止,我已经使用以下宏在 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
.
因为你有一个静态库,问题是默认情况下,只有需要的符号才会链接到你的可执行文件或库中。根据定义,这些不会是您的注册静态对象。
在 Linux 上,使用 --whole-archive
告诉 gcc(或 clang)将完整库集成到最终输出文件中。
在 Windows 上,使用 /WHOLEARCHIVE
。
另一种选择是从您的 executable/shared 库中的某处引用这些对象以强制集成它们。
所以两个选项:
- 整个存档,简单,但集成了静态库中的所有内容
- 手动引用里面的寄存器函数say
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>(®ister_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;
}
}
在 header 函数中,我定义了 REGISTER_EXTERN(SomeClass);
,然后在 cpp 文件的某个地方,我像以前一样使用寄存器宏:
REGISTER(SomeClass)
{
// This code will run before main
}
如果您创建一个 header 文件列出所有 REGISTER_EXTERN
调用,您实际上可以指定寄存器函数的顺序 运行,因为第一次static 被击中,它将初始化并调用该函数。助手确保 REGISTER
函数只被调用一次,因为在不同的地方包含 header 将 re-initialize 每个翻译单元中的静态变量。
这解决了静态库的问题,因为 headers 将被包含并且符号将存在。其次,它确保我可以按正确的顺序进行初始化。
我有一个类型注册系统,用于自定义形式的运行时类型信息。到目前为止,我已经使用以下宏在 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
.
因为你有一个静态库,问题是默认情况下,只有需要的符号才会链接到你的可执行文件或库中。根据定义,这些不会是您的注册静态对象。
在 Linux 上,使用 --whole-archive
告诉 gcc(或 clang)将完整库集成到最终输出文件中。
在 Windows 上,使用 /WHOLEARCHIVE
。
另一种选择是从您的 executable/shared 库中的某处引用这些对象以强制集成它们。
所以两个选项:
- 整个存档,简单,但集成了静态库中的所有内容
- 手动引用里面的寄存器函数say
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>(®ister_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;
}
}
在 header 函数中,我定义了 REGISTER_EXTERN(SomeClass);
,然后在 cpp 文件的某个地方,我像以前一样使用寄存器宏:
REGISTER(SomeClass)
{
// This code will run before main
}
如果您创建一个 header 文件列出所有 REGISTER_EXTERN
调用,您实际上可以指定寄存器函数的顺序 运行,因为第一次static 被击中,它将初始化并调用该函数。助手确保 REGISTER
函数只被调用一次,因为在不同的地方包含 header 将 re-initialize 每个翻译单元中的静态变量。
这解决了静态库的问题,因为 headers 将被包含并且符号将存在。其次,它确保我可以按正确的顺序进行初始化。