C ++ 11是否解决了在动态/共享库边界之间传递std lib对象的问题?(即dll等)?


34

我对C ++的主要抱怨之一是在实践中将标准库对象传递到动态库(例如dll / so)边界之外有多么困难。

std库通常是仅标头的。这非常适合进行一些很棒的优化。但是,对于dll,它们通常是用不同的编译器设置构建的,这可能会影响std库容器的内部结构/代码。例如,在MSVC中,一个dll可能会在打开迭代器调试的情况下构建,而另一个dll会在关闭调试器的情况下构建。这两个dll可能会遇到传递标准容器的问题。如果std::string在界面中公开,则不能保证客户端使用的代码与std::string库的完全匹配std::string

这导致难以调试问题,头痛等。您要么严格控制组织中的编译器设置以防止出现这些问题,要么使用没有这些问题的更简单的C接口。或向您的客户指定他们应使用的预期编译器设置(如果另一个库指定了其他编译​​器设置,则很糟糕)。

我的问题是C ++ 11是否试图做任何事情来解决这些问题?


3
我不知道您问题的答案,但是我可以说您的担忧是共同的。它们是为什么我不在项目中使用C ++的关键,因为我们重视ABI稳定性而不是挤出每个潜在效率周期。
Donal Fellows

2
请区分。在DLLs 之间很难。在SOs 之间,它总是可以正常工作。
Jan Hudec 2012年

1
严格来说,这不是C ++唯一的问题。其他语言可能会出现此问题。
MrFox 2012年

2
@JanHudec我可以保证,SO之间不会像您似乎指示的那样神奇地工作。给定符号可见性以及名称修饰通常如何工作,您可能会遇到更多问题,但是用一个不同的标志/等编译一个.so,并假定可以将其与其他标志链接在程序中是灾难的根源。
sdg

3
@sdg:使用默认标志和默认可见性可以正常工作。如果您更改它们并遇到麻烦,那是您的问题,其他人都没有。
Jan Hudec 2012年

Answers:


20

您是正确的,在任何公共C ++ API中最好避免任何STL(实际上,任何来自模板库的第三方库中的任何东西)。您还希望遵循http://www.ros.org/reps/rep-0009.html#definition上的一长串规则,以禁止ABI破坏,这使得对公共C ++ API进行编程变得很繁琐。

关于C ++ 11的答案是否定的,这个标准并没有解决这个问题。更有趣的是为什么不呢?答案是因为C ++ 17非常令人触动,要实现C ++模块,我们需要导出的模板才能工作,为此,我们需要LLVM类型的编译器(例如clang),可以将完整的AST转储到光盘,然后在任何大型C ++项目中都进行与调用者相关的查找,以处理许多违反ODR的情况-顺便说一句,它包含许多GCC和ELF代码。

最后,我看到了许多MSVC仇恨和亲GCC评论。这些都是错误的信息-ELF上的GCC从根本上来说是无法恢复的,无法生成有效且正确的C ++代码。造成这种情况的原因很多,但我将快速举一个例子:ELF上的GCC无法安全地生成使用Boost.Python编写的Python扩展,其中多个基于Boost.Python的扩展已加载到Python中。这是因为ELF带有全局C符号表的设计完全无法通过设计来防止ODR违反导致段错误,而PE和MachO以及实际上提出的C ++模块规范都使用了每个模块的符号表-顺便说一句,这也意味着极大的进程初始化时间。还有很多其他问题:请参阅我最近在回答的StackOverflow例如,https: //stackoverflow.com/questions/14268736/symbol-visibility-exceptions-runtime-error/14364055#14364055在ELF上从根本上无法修复C ++异常引发。

最后一点:关于互操作不同的STL,这对于许多试图将紧密集成到某些STL实现中的第三方库混合在一起的大型企业用户来说是一个很大的痛苦。唯一的解决方案是C ++处理STL互操作的新机制,当您使用STL互操作时,您也可能会修复编译器互操作,因此您可以(例如)混合使用MSVC,GCC和clang编译的目标文件,并且所有这些都可以正常工作。我会观察C ++ 17的成果,然后看看接下来几年会发生什么-如果什么都做不到,我会感到惊讶。


很好的回应!我只希望Clang改善Windows的兼容性,并且可以设置一个好的默认标准编译器。C ++的文本包含/标头系统太可怕了,我期待模块简化C ++代码组织,无限加速编译时间并提高编译器与违反ODR的捕获程序的互操作性的那一天。
亚历山德罗·斯塔玛托

3
就我个人而言,我实际上期望编译器时间会大大增加。快速遍历模块内AST非常困难,我们可能需要它的内存共享内存缓存。但是,几乎所有其他不好的东西都会变得更好。顺便说一句,头文件肯定存在,当前的C ++模块具有将接口文件一对一映射到头文件的接口文件。而且,自动生成的接口文件将是合法的C ++,因此旧标头仅将C宏过滤掉并吐出来作为接口文件。很好吗?
Niall Douglas

凉!我对模块有很多疑问。模块系统会考虑文本包含还是符号包含?使用当前的include指令,编译器必须为每个源文件一次又一次地重新编译成千上万行代码。模块系统是否将允许有一天的代码没有前向声明?它会改善/简化构建工具吗?
亚历山德罗·斯塔玛托

2
-1表示所有第三方模板都是可疑的。更改配置与配置的事物是否是模板无关。
DeadMG

1
@Alessandro:建议的C ++模块显式禁用C宏。您可以使用模板,也可以使用nowt。提出的接口是合法的C ++,仅是自动生成的,并且可以选择进行预编译以提高解析速度,即,不要期望比现有的预编译标头有任何提速。最后两个问题,我实际上不知道:取决于:)
Niall Douglas

8

规范从未出现过此问题。这是因为它具有称为“一个定义规则”的概念,该概念要求每个符号在运行的过程中都具有一个唯一的定义。

Windows DLL违反了此要求。这就是为什么存在所有这些问题的原因。因此,由Microsoft来解决它,而不是由C ++标准化委员会来解决。Unix从未遇到过这个问题,因为共享库在那里的工作方式不同,并且默认情况下符合一个定义规则(您可以明确地打破它,但是显然,只有在您知道自己负担得起并且需要挤出几个额外的周期的情况下,才这样做)。

Windows DLL违反一个定义规则,因为:

  • 它们在静态链接时间内使用哪个符号的硬库进行硬编码,并在定义符号的库中静态解析符号。因此,如果在多个共享库中生成相同的弱符号,并且与在单个进程中使用的库相比生成的弱符号,则动态链接程序将没有机会合并这些符号。通常,此类符号是模板实例的静态成员或类标记,因此在不同DLL中的代码之间传递实例时会引起问题。
  • 他们在编译过程中已硬编码是否将符号从动态库中导入。因此,静态链接到某个库的代码与动态链接到同一库的代码不兼容。

Unix使用ELF格式导出会隐式导入所有导出的符号,以避免出现第一个问题,并且直到静态链接时间才避免静态和动态解析的符号之间的区别,从而避免出现第二个问题。


另一个问题是编译器标志。对于由多个编译单元组成的任何程序都存在该问题,而不必涉及动态库。但是,在Windows上情况更糟。在Unix上,无论是静态链接还是动态链接都没有关系,没有人以静态方式静态链接标准运行时(在Linux中甚至可能是非法的),并且没有特殊的调试运行时,因此一个构建就足够了。但是Microsoft实现静态和动态链接,调试和发布运行时以及其他一些选项的方式意味着它们引起了所需库变体的组合爆炸。再次是平台问题,而不是C ++语言问题。


2
@DougT .: GCC与它无关。ABI拥有的平台。在ELF(大多数Unices使用的对象格式)中,共享库导出所有可见符号并导入它们导出的所有符号。因此,如果在多个库中生成了某些内容,则动态链接器将对所有内容使用第一个定义。简单,优雅且实用。
Jan Hudec 2012年

1
@MartinBa:没有要合并的内容,但是没有关系,只要它是相同的,并且只要不应该首先合并即可。是的,如果您在ELF平台上使用了不兼容的编译器设置,那么您将在任何地方和任何地方都遇到相同的麻烦。即使不使用共享库,这里也有点题外话。
Jan Hudec 2012年

1
@Jan-与您的答案有关。您写道:“ ...一个定义规则... Windows DLL违反了此要求...共享库在[UNix]上的工作方式有所不同...“,但提出的问题与std-lib东西有关的问题(在标头中定义) Unix上没有问题的原因与SO与DLL无关,但事实是,在Unix上(显然),标准库只有一个兼容版本,而Windows MS则选择了不兼容(调试)版本(带有扩展检查等)。
马丁·巴

1
@MartinBa:不,Windows上存在问题的主要原因是Windows上使用的导出/导入机制无法在所有情况下正确合并模板类的静态成员和类impimimenta,并且不能合并静态和动态链接的符号。相比之下,多种库变体使情况更糟,但是主要的问题是C ++需要Windows动态链接程序所没有的链接程序的灵活性。
Jan Hudec 2012年

4
我认为DLL规范已被打破,并且对Msft进行“修复”的相应要求被放错了位置。DLL不支持C ++的某些功能这一事实并不是DLL规范的缺陷。DLL是一种与语言无关,与供应商无关的打包机制,并且是ABI,可将入口点暴露给机器代码(“函数调用”)和数据blob。他们从不打算原生支持任何特定语言的高级功能。不是Msft的错误,也不是DLL规范的错误,有人希望它们成为其他东西。
Euro Micelli

6

没有。

替换标头系统还有很多工作要做,该功能称为模块,可能会对此产生影响,但肯定不会很大。


2
我认为标题系统不会对此产生任何影响。问题在于Windows DLL违反了一个定义规则(这意味着它们不遵循C ++规范,因此C ++委员会对此无能为力),并且Windows中的标准运行时变体太多,C ++委员会可以做到这一点。什么都不要做。
Jan Hudec 2012年

1
不,他们没有。他们怎么可能甚至没有提到那种说明。除此之外,当(Windows)程序与Windows dll链接时,ODR被满足:所有可见(导出)的符号都必须服从ODR。
Paul Michalik 2013年

@PaulMichalik C ++确实涵盖了链接(第9阶段),在我看来,至少DLL / SO的加载时链接属于第9阶段。这意味着具有外部链接(无论是否导出)的符号应链接并遵循ODR。与LoadLibrary / dlopen的动态链接显然不属于那些要求。
bames53 2013年

@ bames53:恕我直言,规范太弱以至于无法进行此类声明。一个.DLL /。所以可以被看作是对自己的一个“节目”。然后,规则得到了满足。该标准忽视了诸如在运行时加载其他“程序”之类的东西,以至于与此相关的任何陈述都是相当随意的。
Paul Michalik 2013年

@PaulMichalik如果可执行文件需要加载时链接,则在加载时链接之前,还有一些外部实体尚未解决,并且缺少执行所需的信息。LoadLibrary和dlopen的是外面的规范,但负载时链接很显然必须是相9的一部分
bames53
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.