Boost 单元测试框架 dll 导出的 std::basic_ostringstream 导致“已定义符号”错误

std::basic_ostringstream exported by Boost unit test framework dll results in an "already defined symbol"-error

提问人:Sascha 提问时间:6/20/2016 最后编辑:Sascha 更新时间:2/25/2021 访问量:700

问:

我使用 Visual Studio 2012。我的设置是这样的:

  • 针对某些.exe的some.lib链接
  • some.lib 针对 some_test.exe 的链接

我在构建some_test.exe时使用BOOST_TEST_DYN_LINK。使用 some.lib 和 test.exe 的 BOOST_ALL_DYN_LINK 结果相同。

我已经用 /MD(多线程 DLL)构建了 some_test.exe、some.exe 和 some.lib。我已经使用 runtime-link=shared 构建了提升库。所有这些都由 VC11 (Visual Studio 2012) 构建和链接。

问题是,在 some.lib 中,我想使用局部变量

std::ostringstream someStream;

some.exe 链接正常。但是当链接动态链接到 boost 单元测试框架 (1.59) 的 some_test.exe 时,它给了我 3 个错误 (LNK2005):

错误

boost_unit_test_framework-vc110-mt-1_59.lib(boost_unit_test_framework-vc110-mt-1_59.dll) : error LNK2005: "public: __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >(int)" (??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z) already defined in some.lib(some.obj) 
boost_unit_test_framework-vc110-mt-1_59.lib(boost_unit_test_framework-vc110-mt-1_59.dll) : error LNK2005: "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(void)const " (?str@?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ) already defined in some.lib(some.obj) 
boost_unit_test_framework-vc110-mt-1_59.lib(boost_unit_test_framework-vc110-mt-1_59.dll) : error LNK2005: "public: void __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::`vbase destructor'(void)" (??_D?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAAXXZ) already defined in some.lib(some.obj) 
some_test.exe : fatal error LNK1169: one or more multiply defined symbols found 

使用 msvc14 (Visual Studio 2015) 时也会发生同样的情况

boost_unit_test_framework-vc140-mt-1_59.lib(boost_unit_test_framework-vc140-mt-1_59.dll) : error LNK2005: "public: __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char>>::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >(int)" (??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z) already defined in some.lib(some.obj)
boost_unit_test_framework-vc140-mt-1_59.lib(boost_unit_test_framework-vc140-mt-1_59.dll) : error LNK2005: "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(void)const " (?str@?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ) already defined in some.lib(some.obj)
boost_unit_test_framework-vc140-mt-1_59.lib(boost_unit_test_framework-vc140-mt-1_59.dll) : error LNK2005: "public: void __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::`vbase destructor'(void)" (??_D?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAAXXZ) already defined in some.lib(some.obj)
some_test.exe : fatal error LNK1169: one or more multiply defined symbols found

奇怪的依赖关系

我在文件 boost_unit_test_framework-vc110-mt-1_59 上运行了 Dependency Walker.dll

??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@$$QEAV01@@Z
std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int)
std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >(int)

或装饰:

??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@$$QEAV01@@Z
??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@1@H@Z
??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z

为了进行比较,我从以下位置下载了 boost_unit_test_framework-vc110-mt-1_61.dll

https://sourceforge.net/projects/boost/files/boost-binaries/

并且该 DLL 还会导出那些冲突的 ostringstream 符号

 ??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@$$QEAV01@@Z
 std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int)
 std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >(int)

因此,对于测试框架来说,这似乎是正常行为。 不过,对我来说,将这些符号导出到 dll 似乎不是一个好主意。

我还做了一个dumpbin /symbols some.lib

在那里,我发现了相互冲突的符号:

2AFC 00000000 SECT1183 notype ()    External   | ?str@?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(void)const )
43D1 00000000 SECT16FA notype       Static     | $unwind$?str@?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ
43D4 00000000 SECT16FB notype       Static     | $pdata$?str@?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ

2AFA 00000000 SECT6AF notype ()    External    | ??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z (public: __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >(int))
43B6 00000000 SECT16F1 notype       Static     | $unwind$??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z
43B9 00000000 SECT16F2 notype       Static     | $pdata$??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z
43BC 00000000 SECT16F3 notype       Static     | $cppxdata$??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z
43BF 00000000 SECT16F4 notype       Static     | $stateUnwindMap$??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z
43C2 00000000 SECT16F5 notype       Static     | $ip2state$??0?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAA@H@Z

2B0E 00000000 SECTA3C notype ()    External    | ??_D?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAAXXZ (public: void __cdecl std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::`vbase destructor'(void))
4446 00000000 SECT1721 notype       Static     | $unwind$??_D?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAAXXZ
4449 00000000 SECT1722 notype       Static     | $pdata$??_D?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QEAAXXZ

据我了解,(完全匹配)符号在 some.lib 中标记为“外部”。因此,它们不是从运行时静态链接到 some.lib,而是动态链接。

已知的解决方法

我可以解决该问题,要么使用而不是在 some.lib 的源代码中。我想我可以忍受这一点,但对于任何维护者来说,为什么不允许使用 ostringstream 都很难理解。std::stringstreamstd::ostringstream

或者,我可以使用 Linker-Flag 进行some_test.exe并将错误LNK2005降级为警告LNK4006。但我不喜欢永久警告,特别是如果它们只是被屏蔽的错误。/FORCE:MULTIPLE

问题

使用boost_unit_test_framework而不出现这些链接器错误的正确方法是什么?

boost 是故意导出还是我应该提交错误报告?std::basic_ostringstream

我是不是问错了问题?

杂项

MSVC 的行为似乎在版本 2010 中已更改。在此之前,没有错误。https://social.msdn.microsoft.com/Forums/vstudio/en-US/191de00a-53c9-4bd9-9cb6-e844eb224ca2/lnk2005-when-using-stdostringstream?forum=vclanguage

在 8 天没有对 SO 做出答复或评论后,我在 boost-user 邮件列表上打开了一个线程。当然,如果找到答案,我会在 SO 和邮件列表之间分享。http://lists.boost.org/boost-users/2016/06/86332.php(截至2017年8月17日,邮件列表中仍然没有给出解决方案)

现在,1 年后,我升级到 Visual Studio 2015 并得到相同的行为。

C Visual-C++ 提升 DLL

评论

0赞 zahir 7/4/2016
可以肯定的是,您是否定义了“BOOST_ALL_DYN_LINK”符号?
0赞 Sascha 7/4/2016
我已经为 some_test.exe 定义了BOOST_TEST_DYN_LINK,但也重建并链接了为 some.lib 和 some_test.exe 设置BOOST_ALL_DYN_LINK。3 错误消息保持不变。
0赞 eh9 7/4/2016
你试过VS 2015吗?如果问题出现在一个编译器版本中,则它可能已在另一个编译器版本中消失。
0赞 Sascha 7/6/2016
@eh9我刚刚尝试了VS 2015,并得到了相同的结果。我在问题中添加了链接器错误。

答:

0赞 Markus Schumann 8/16/2017 #1

我敢打赌,你会拉入两个不同的 C 运行时。 一个通过 Boost,另一个通过您的项目。 请检查您的项目some_test.exe->配置属性-> C/C++ -> 代码生成 -> 运行时库,并尝试多线程 DLL。

评论

0赞 Sascha 8/17/2017
我检查了设置(第一百次:-))。它已在所有项目中设置为 /MD。
0赞 Sahib Yar 8/17/2017 #2

通过谷歌搜索您的问题,我发现了以下建议,如果它们可以帮助您的话


添加到 depend-projects 宏,并在主项目中添加到链接器输入必要的库。 将 /FORCE:MULTIPLE 添加到链接器命令行选项中。BOOST_ALL_NO_LIB

来自 MSDN:“使用 /FORCE:MULTIPLE 创建输出文件,无论 LINK 是否为符号找到多个定义。

试试这个:

该选项会导致 Boost Build 构建所有 支持的库变体。用于调试。--build-type=complete/MTd

您也可以查阅此页面

评论

0赞 Sascha 8/17/2017
我用BOOST_ALL_NO_LIB尝试了您的建议,但它并没有改变错误。正如我在问题中提到的,/FORCE:MULTIPLE 将永久错误更改为永久警告。我希望我的代码编译时没有警告。
0赞 Richard Keene 2/25/2021 #3

由于尝试了许多方法在我的项目中包含提升单元测试,我遇到了类似的问题,并且包含重复的包含文件以及我的unit_test_main的重复定义。