使用clang++ -stdlib=libc++编译的库与libstdc++一起使用

74

我正在Mac OS X (10.8.2)下使用C++编程,最近需要使用C++11特性,可以通过clang++编译器和libc++ stdlib实现。但是,我还需要使用一些使用了libstdc++链接的旧库(来自MacPorts)。

在此过程中,我遇到了链接错误,因为我使用的旧库头文件,例如 std::string,需要解析成 std::__1::basic_string(即std::string的libc++实现),而不是std::basic_string实现。

在开发中是否有一种混合两个库的方法(例如使用一些预处理器标志)?

1个回答

98
你所看到的是使用内联命名空间来实现ABI版本控制。
这意味着: libstdc++中的std::string是一种不同的数据结构,而libc++中的std::string则不是。前者是引用计数设计,而后者不是。虽然它们具有API兼容性,但它们不具备ABI兼容性。这意味着,如果你使用libstdc++构造了一个std::string,并将其传递给链接到libc++的其他代码,接收方会认为它有一个libc++的std::string。也就是说,接收方并不知道它应该增加或减少引用计数。
如果没有内联命名空间,将会产生运行时错误。你最好的希望只能是崩溃。有了内联命名空间,这个运行时错误将被转换为链接时错误。
对于程序员而言,libstdc++的std::string和libc++的std::string看起来像是相同类型。但对于链接器而言,它们看起来像是完全不同的类型(提示是std::__1命名空间)。连接器的视角是正确的。它们完全不同的类型。
因此,你可以操纵一些预处理器标志使事情能够链接。但是那样你将很难调试结果的运行时错误。
唯一的方法是使这些dylib之间的接口不涉及std::类型,例如你可以传递char数组。你甚至可以在libstdc++链接的代码和libc++链接的代码之间传递内存所有权(它们都将下降到相同的malloc池中)。

7
总之,不能使用遗留库(即链接到libstdc++的库)与libc++一起使用,这是一个非常严重的限制。 - user1690715
13
有一个最大的希望:只要您不在动态库边界传递版本化符号,就可以在进程中混合使用这两个库。如果您不小心这样做,会在链接时捕获错误。您可以在动态库边界上抛出派生自std::exception的异常并在动态库边界之间转移内存所有权。我怀疑这可能比gcc自己的4.2版本提供了更好的ABI兼容性。请注意,根据std规范,甚至std::string在C++98/03和C++11之间也不具有ABI兼容性。前者被广泛地引用计数,而后者则被禁止。 - Howard Hinnant
4
另一个例子:早期的gcc std::list版本不与C++11规范中的std::list兼容。换句话说,你关于libc++的评论同样适用于早期的libstdc++版本(如果不是更适用)。libc++与早期的libstdc++的ABI不兼容的程度与符合C++11标准的libstdc++相同。 - Howard Hinnant
3
更准确的说法是,如果两个运行时在其API中公开STL对象,则构建针对这两个运行时的互操作库没有希望 - 这一点始终是一个非常冒险的建议。 - marko
3
能否为std::string和std::_1::string之间创建一种赋值重载? - Michael
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接