首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++全局变量在通过静态库链接时不会初始化,但在使用源代码编译时可以

C++全局变量在通过静态库链接时不会初始化,但在使用源代码编译时可以
EN

Stack Overflow用户
提问于 2012-02-27 12:12:09
回答 2查看 15.9K关注 0票数 25

我已经创建了一个系统,它基于全局实例的构造函数自动将函数对象(函数)注册到映射中。

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

注册器类的定义如下:

代码语言:javascript
运行
复制
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));
        }
};

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

代码语言:javascript
运行
复制
namespace one_way
{
    static registrar <in_out_map_type, handler>
        post_receiver(in_out_map_type::instance(), command, handlers());
}

如果我将所有的cpp和main.cpp一起编译,所有的工作都很好。但是,如果我将cpp文件编译为静态库并将其链接到main.cpp,则注册不起作用。

我在Windows &和Ubuntu11.10上用VC10和GCC4.61进行了测试。两者都失败了。

我找到了a thread with the same problem,但OP没有说他是否解决了这个问题。

我错过了什么吗?

编辑

感谢所有的回复,包括评论。

每一个回复确实帮助我更多地思考和深入研究这种方法。在所有的研究和试验之后,我最终放弃了依赖全局/静态变量来跨越二进制边界自注册的想法,因为没有可移植的方法来保证它会工作。

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-02-27 12:21:19

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

一旦你知道了它们是如何处理acrtused的,对你的全局变量做同样的事情来强制它链接。

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

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

here上寻找GCC的答案。

在MSVC10中查找here

票数 -1
EN

Stack Overflow用户

发布于 2012-07-05 08:03:28

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

MSVC的更长答案:

翻译单元中的静态变量在翻译单元中的任何常规代码执行之前被初始化。实际上,初始化是在加载包含可执行文件或动态库的时候进行的。当您的\ call ()被调用时,或者您对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的实体,这可以在包含实体的源代码中完成:

代码语言:javascript
运行
复制
#pragma comment(linker, "/include:DummyForLinkProblem")

当前受此问题影响的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实体中包含来自已知引用代码的适当头文件。这将导致非执行引用的发生,并且事情将按预期工作。

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9459980

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档