C++全局变量在通过静态库链接时未初始化,但在使用源编译时可以正常

t.g*_*.g. 21 c++ initialization global-variables static-libraries static-linking

我创建了一个系统,它根据全局实例的构造函数自动将函数对象(仿函数)注册到映射中.

在定义仿函数的每个cpp文件中,有一个注册器类实例的全局实例,用于将仿函数注册到单个std::map<int, std::function<...> >对象.

这是注册商类的定义:

template
<
    typename map_type,
    typename handler_type
>
struct registrar
{
    registrar
        (
             map_type& map_object,
             boost::uint16_t cmd_code,
             const handler_type& handler
        )
        {
          map_object.insert(std::pair<boost::uint16_t, handler_type>(cmd_code, handler));
        }
};
Run Code Online (Sandbox Code Playgroud)

在每个.cpp文件中.全局实例定义如下:

namespace one_way
{
    static registrar <in_out_map_type, handler>
        post_receiver(in_out_map_type::instance(), command, handlers());
}
Run Code Online (Sandbox Code Playgroud)

如果我将所有cpp与main.cpp一起编译,那么一切正常.但是如果我将cpp文件编译成静态库并将其链接到main.cpp,则注册不起作用.

我在Windows和Ubuntu 11.10上使用VC10和GCC4.61进行了测试.两者都失败了

我发现一个线程有同样的问题,但OP没有说是否解决了.

我错过了什么吗?


编辑


感谢所有回复,包括评论.

每一个回复确实帮助我更多地思考并深入研究这种方法.经过所有的研究和试验,我终于放弃了依赖全局/静态变量进行二元边界自注册的想法,因为没有可靠的方法来保证它能够正常工作.

我最后的方法是将注册保持在一个二进制文件中.

And*_*een 9

对于Android NDK工作的简短回答,任何受此问题影响的静态库都应该添加到LOCAL_WHOLE_STATIC_LIBRARIES变量中 - 然后它们将使用该 -Wl,--whole-archive标志引用,并且不会被剥离.

MSVC的答案更长:

在翻译单元中的任何常规代码执行之前初始化翻译单元中的静态变量.实际上,在加载包含可执行文件或动态库时会发生初始化.调用\ c main()或调用 LoadLibrary()/dlopen()完成时,任何静态变量都将被初始化.

问题,如MSDN所述:

声明中的构造函数和全局函数或静态方法的赋值不会创建引用,也不会阻止/ OPT:REF消除.当不存在对数据的其他引用时,不应依赖此类代码的副作用.

将来自多个翻译单元的目标代码放在单个文件中是很方便的,静态库通常用\ c .lib或\ c .a后缀命名.MSVC链接器对静态库执行依赖性分析,并且不包括未包含实体引用的代码.

在这种情况下,使用静态变量声明并导致工厂对象注册的常见模式可能会失败 - MSVC链接器将静态视为无法访问并将其从结果中剥离.

解决方案

一个有用的谷歌搜索: http://www.google.com/search?q=msvc+factory+static+library

一种解决方案是/OPT:NOREF在包含实体上设置链接器标志.但是,这是一个全有或全无设置,并要求所有包含的库都是完全可链接的.

如果包含静态的文件中的某些内容由包含实体引用(直接或间接),则必须通过语言规则保留静态本身.

最基本的方法是在文件中放置一个虚函数,并从已知被认为可达的某个地方引用该函数.

另一种方法是使用/INCLUDE链接器标志来引用问题文件中的实体.假设一个名为DummyForLinkProblem的实体,可以在include实体的源代码中完成:

#pragma comment(linker, "/include:DummyForLinkProblem")
Run Code Online (Sandbox Code Playgroud)

ZooLib的解决方案

目前受此问题影响的ZooLib实体是ZFile_Win.cpp,ZGRgnRep_HRGN.cpp,ZNet_Internet_WinSock.cpp,ZStreamRWCon_SSL_Win.cpp,ZTextCoder_Win.cpp和ZUnicode_Normalize_Win.cpp中的实体.

我们#include ZCompat_MSVCStaticLib.h在相应的头文件中,并在每个文件中放入一个 ZMACRO_MSVCStaticLib_Reference(ModifiedFileName).在cpp文件中我们放了一个 ZMACRO_MSVCStaticLib_cpp(ModifiedFileName).在ModifiedFileName通常是与领先的Z和文件扩展名删除,因为在使用相同的样式文件名ZCONFIG_API_XXX的宏.

要确保您的可执行文件或库不剥离这些实体,只需#include包含实体中已知引用代码的相应头文件.这将导致发生不执行的引用,并且事情将按预期工作.


Jim*_*imR 0

我相信您的库中的目标文件没有被链接。看看微软如何处理 acrtused 符号(搜索不区分大小写,我不记得大小写,而且这台机器上没有 MSVC)。

一旦您知道它们如何处理 acrtused,请对全局变量执行相同的操作以强制其链接。

如果我找到答案就会更新。

这里有几种强制事物以某种强制顺序链接和初始化的可能性。

在这里寻找海湾合作委员会的答案。

在此处查找MSVC10。