我正在研究某个C ++库(或更多框架)。我想让它落后 兼容以前的版本,不仅保留了API兼容性,还保留了ABI(就像Qt那样出色的工作)。
我使用Boost的许多功能,对我来说,这使得向后兼容性变得不可能,除非我强迫用户拥有完全相同(有时是旧版本)的Boost。
有没有办法(没有重写1/2的Boost)在其命名空间周围做一些“前缀”/重命名它以防止它干扰用户版本的Boost?
例如我的libXYZ使用Boost 1.33并且它有类boost::foo
。在版本1.35 boost::foo
已升级并添加了新成员,因此,boost::foo
来自1.33和1.35
不兼容ABI。因此,libXYZ的用户必须使用Boost 1.33或重新编译libXYZ
提升1.35(可能已经以XYZ无法编译的方式破坏了某些API)。
注意:我说的是带有ELF的UNIX / Linux操作系统,其中动态链接类似于静态链接,因此您无法链接两个不同版本的库,因为符号会干扰。 / p>
我可能想到的一个合适的解决方案是将Boost放在其他私有命名空间中。因此,libXYZ将使用::XYZ::boost::foo
而不是::boost::foo
。这可以防止与用户可能使用的其他版本的Boost发生冲突。
因此,libXYZ将继续使用Boost 1.33与其他命名空间静态或动态链接,假设它:
有没有办法用Boost做这些事情?
编辑:最后,我决定创建一个脚本,将源中的所有提升符号重命名为某个自定义符号。
基本原理:简化构建过程,独立于编译器可见性支持,此外,它的可见性仅适用于动态库,对于静态,这不起作用,因此我需要为每种类型的库提供单独的构建和依赖。
脚本可在那里找到:http://art-blog.no-ip.info/files/rename.py
编辑2:最新版本的Boost BCP支持命名空间重命名。
答案 0 :(得分:29)
基本上,只需确保库的公共接口不会暴露Boost。您可以随时在内部使用它。通常,让库的接口依赖于另一个库是不好的(除非它依赖于像STL这样的标准库)。 Boost几乎适合“标准”库类别,但其ABI变化太大,以至于您的界面不应该使用它。
为确保不暴露Boost符号,您可以执行以下操作:
一个。使用-fvisibility=hidden
进行编译,并使用__attribute__((visibility("default")))
标记所有公共符号。您可以使用宏来简化这一过程:
#define ABI __attribute__((visibility("default")))
B中。做这样的事情:
#pragma GCC visibility push(hidden)
#include <boost/whatever.hpp>
#pragma GCC visibility pop
您还应将其包裹在您不想导出的所有其他内部符号周围,或使用__attribute__((visibility("hidden")))
声明此内容。同样,您可以使用宏来简化这一过程:
#define INTERNAL __attribute__((visibility("hidden")))
在这些选项中,我更喜欢A,因为它会让您明确考虑导出哪些符号,因此您不会意外导出您不想要的内容。
顺便说一句,您可以在Ulrich Drepper的How to Write Shared Libraries中找到有关制作DSO的更多信息。
答案 1 :(得分:5)
一般来说,除了标准的C绑定之外,你不能依赖C ++中的任何类型的ABI。但根据您做出的假设,您可以在界面中使用越来越多的C ++。
我发现了this关于让您的API变成稳定ABI的步骤的精彩文章。例如,永远不要在您的接口上传递标准C ++库(或Boost)数据类型;如果修复了一个小错误,它可能会收支平衡。
发布ABI兼容API时需要注意的一些问题示例如下:
.h
文件中发布的内容以及分配发生的位置。如果您关注链接文章,您将找到解决这些问题和其他问题的方法。
修改强>:
我还发现了一个有趣的article published by Microsoft,它描述了COM接口如何工作,通过采用C ++项目并将其转换为COM。我认为微软开发COM的主要原因之一是解决C ++所具有的脆弱二进制接口问题,因此他们可以使用面向发布对象的API发布DLL。
答案 2 :(得分:3)
考虑使用abi-compliance-checker工具来维护稳定的API / ABI接口。
答案 3 :(得分:0)
你应该可以这样做:
namespace XYZ
{
#include <boost/my_library.hpp>
}
它应该将boost标头转储到名称空间XYZ中。但请注意,这仅适用于仅限标头的库。