为什么要二进制兼容性?

4

我正在学习 PIMPL 惯用语。它的一个优点是二进制兼容性。我想知道二进制兼容性的优点是什么。谢谢!

(注:该段内容已经是中文,无需翻译)

提供声明的参考文献 - Cheers and hth. - Alf
3个回答

3
它避免了脆弱的二进制接口问题。具体如下:
  1. 程序使用库。

  2. 用户升级库。升级会改变库的二进制接口中的某些内容。

  3. 程序现在无法工作,除非重新编译,因为它是基于旧的二进制接口构建的。

PIMPL惯用语的优点之一是,它允许您将通常是类公共接口的东西移动到其私有接口中(实际上是一个私有类的接口)。您可以更改私有接口而不会破坏二进制兼容性。

2

v1.0

让我们考虑一下 libMagic 库 v1.0 中的以下类

//MagicNumber.h
struct MagicNumber {
  MagicNumber();
  int get();
  int id;
}
//MagicNumber.cpp
int MagicNumber::get() {
  return 42;
}

应用程序代码:
void foo() {
  MagicNumber m;
  int i = 27;
  std::cout << m.get() + i << '\n';
}

当上述应用程序代码通过动态链接 libMagic.so 进行编译时,foo 函数将被编译如下。
foo:
   Allocate 4 bytes space in stack for m
   Allocate 4 bytes space in stack for i and write 27 in it
   Call MagicNumber::get //This address is resolved on application startup.
   ... //Rest of processing

v1.0.1

现在,当libMagic发布新版本v1.0.1时,实现方面有以下更改,但头文件没有更改。
//MagicNumber.cpp
int MagicNumber::get() {
  return call_real_magic_number_fn();
}

应用程序无需重新编译,因此无需更新。它将自动调用具有新实现的更新版本。

v1.1.0 - 二进制不兼容

假设库有另一个更新版本(v1.1.0),以下是更改内容。

//MagicNumber.h
struct MagicNumber {
  MagicNumber();
  int get();
  int id;
  int cache; //Note: New member
}
//MagicNumber.cpp
int MagicNumber::get() {
  if(cache != 0) return cache;
  cache = call_real_magic_number_fn();
  return cache;
}

现在,编译后的foo函数将不会为新增成员分配空间。该库已经破坏了二进制兼容性。
foo:
   Allocate 4 bytes space in stack for m //4 bytes is not enough for m
   Allocate 4 bytes space in stack for i and write 27 in it.
   Call MagicNumber::get //This address is resolved on application startup.
   ... //Rest of processing

发生的是未定义行为。可能会将i=27写入缓存变量中,MagicNumber::get将返回27。但任何事情都有可能发生。
如果 libMagic 使用了 PIMPL 模式,那么所有成员变量将属于 MagicNumberImpl 类,其大小不会暴露给应用程序代码。因此,库作者可以在后续版本的库中添加新成员,而不会破坏二进制兼容性。
struct MagicNumberImpl;
struct MagicNumber {
  MagicNumber();
  private:
    MagicNumberImpl* impl;
}

上述类定义在新版本中不会改变,当向类添加新成员时指针的大小也不会改变。
注意: 二进制兼容性只涉及以下情况:
1.使用动态链接库进行链接(例如在Linux中使用.so文件)。 2.更新库到新版本而不重新编译应用程序代码。如果库和二进制文件在同一项目中-您的构建系统将自动重新编译和更新两者。因此,无需担心这个问题或PIMPL。
注2:还有另一种解决相同问题的方法,即使用名称空间的ABI版本控制。

0
PIMPL技巧的优势不在于二进制兼容性,而是如果您更改类的实现甚至布局,则需要重新编译的次数会减少。例如,如果您向类添加新的数据成员,则会更改类的布局,通常需要重新编译类的所有客户端,但如果使用PIMPL技巧,则无需这样做。
二进制兼容性更多地涉及与多个编译器(和编译器版本)兼容,C++中唯一的方法是使用由类实现的接口(抽象类),这些类不会暴露给客户端。这是因为所有编译器都以相同的方式实现抽象类的虚函数表布局。许多API(例如DirectX API)都是以这种方式公开的,以便可以与任何编译器一起使用。

1
你不需要重新编译的原因正是因为你保留了二进制兼容性。 - Jørgen Fogh

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