25

我正在研究某个 C++ 库(或更多框架)。我想让它向后兼容以前的版本,不仅保留 API 兼容性,还保留 ABI(就像 Qt 所做的出色工作一样)。

我使用了 Boost 的许多功能,在我看来,这使得向后兼容变得不可能,除非我强迫用户拥有完全相同(有时是旧的)版本的 Boost。

有没有办法(不重写 Boost 的 1/2)在其命名空间周围制作一些“前缀”/重命名它以防止它干扰用户版本的 Boost?

例如我的 libXYZ 使用 Boost 1.33 并且它有 class boost::foo. 在 1.35 版本boost::foo中升级并添加了新成员,因此,boost::foo从 1.33 和 1.35 开始不兼容 ABI。因此,libXYZ 的用户必须使用 Boost 1.33 或使用 Boost 1.35 重新编译 libXYZ(这可能已经以 XYZ 无法编译的方式破坏了某些 API)。

注意:我说的是带有 ELF 的 UNIX/Linux 操作系统,其中动态链接类似于静态链接,因此您不能链接两个不同版本的库,因为符号会干扰。

我可能想到的一种合适的解决方案是将 Boost 放在其他一些私有名称空间中。因此,libXYZ 将使用::XYZ::boost::foo而不是::boost::foo. 这将防止与用户可能使用的其他版本的 Boost 发生冲突。

因此,libXYZ 将继续与 Boost 1.33 一起工作,静态或动态链接到它与其他命名空间,假设它:

  • 不会在外部公开 Boost API。
  • 将保持公开 API 的稳定私有版本。

有没有办法用Boost做这样的事情?

编辑:最后我决定创建一个脚本,将源中的所有 boost 符号重命名为一些自定义符号。

基本原理:简化构建过程,独立于编译器可见性支持,而且它的可见性仅适用于动态库,对于静态这不起作用,所以我需要为每种类型的库单独构建和依赖。

该脚本在那里可用: http: //art-blog.no-ip.info/files/rename.py

编辑 2: Boost BCP 的最新版本支持命名空间重命名。

4

4 回答 4

30

基本上,只要确保你的库的公共接口不会暴露 Boost。您可以随时在内部使用它。一般来说,让一个库的接口依赖于另一个库是不好的(除非它依赖于像 STL 这样的标准库)。Boost 几乎适合“标准”库类别,但它的 ABI 变化如此之大,以至于您的界面不应该使用它。

为确保您不会暴露 Boost 符号,您可以执行以下操作:

A. 编译-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 的更多信息。

于 2009-05-07T20:39:16.167 回答
5

通常,除了标准 C 绑定之外,您不能依赖 C++ 中的任何类型的 ABI。但是根据您做出多少假设,您可以在界面中使用越来越多的 C++。

我发现这篇很棒的文章介绍了如何让你的 API 变成稳定的 ABI。例如,永远不要在界面中传递标准 C++ 库(或 Boost)数据类型;即使对库进行了小错误修复,它也可能会收支平衡。

发布 ABI 兼容 API 时需要注意的一些问题示例如下:

  • Windows 调试堆。您必须确保所有分配和解除分配都位于“模块”(即可执行文件或 DLL)的同一侧。
  • 脆弱的二进制接口问题。即使系统的两端始终使用相同的编译器和库,您在 C++ 中也必须小心您在.h文件中发布的内容以及分配发生的位置。

如果您关注链接的文章,您将找到这些问题和其他问题的解决方案。

编辑

我还发现Microsoft 发表的一篇有趣的文章描述了 COM 接口的工作原理,将 C++ 项目转化为 COM。我相信微软开发 COM 的主要原因之一是为了解决 C++ 的脆弱二进制接口问题,因此他们可以发布带有面向对象 API 的 DLL。

于 2009-05-08T00:42:13.853 回答
3

考虑使用abi-compliance-checker工具来维护稳定的 API/ABI 接口。

于 2009-08-07T23:12:13.113 回答
0

你应该能够做这样的事情:

namespace XYZ
{
#include <boost/my_library.hpp>
}

它应该将 boost 标头转储到命名空间 XYZ 中。但是请注意,这仅适用于仅标头库。

于 2009-05-07T20:46:03.420 回答