如果 C++ 中的 DLL 不支持二进制封装,那么什么时候可以使用它们呢?

If DLL's in C++ don't support binary encapsulation, when is it ever right to use them?

所以我正在阅读 COM 和 DLL,似乎如果我要更新 DLL 的实现(如果所需内存超过旧需要),我需要重新编译使用带有更新 DLL 的 DLL 的程序。所以我只是想知道,什么时候使用 DLL 而支持 COM 是正确的?似乎几乎不可能重新编译所有程序,只是因为它们更新了依赖项。

此外,只是一个快速的附带问题:为什么二进制封装在 .NET 中不是问题?为什么我可以更改在 .NET 中找到的 DLL 而无需重新编译我的旧项目,而不使用 pimpl/opaque 指针和诸如此类的东西?

二进制兼容性要求调用方和被调用方就通用 ABI(应用程序二进制接口)达成一致。

在 Windows 和 Linux 上,C 调用的标准 ABI 由 OS 指定,它允许从任何其他语言调用 C 库。这也使得 C++ 对 DLL 开发很有用,只要 C++ 仅在 inside DLL 和外表面使用 extern "C" 和 C 兼容类型,如指针和基元和C 兼容类型的普通旧数据组合。

在 Linux 和 Windows 上,C++ ABI 与 C++ 运行time 库关联。在 Linux 上,人们习惯于自欺欺人地认为 C++ 共享对象也有一个 OS 提供的 ABI,因为只有一个广泛使用的 C++ 库——g++ 的 libstdc++,和因为该库竭尽全力保留相同的 ABI。所以 C++11 之前的 libstdc++ ABI 是事实上的标准,没有人 运行 因混合 ABI 而陷入麻烦。现在 C++11 强制 libstdc++ 打破二进制兼容性,并且 clang 的 libc++ 越来越流行,因此 Linux 开发人员面临着 Windows 开发人员已经熟悉的问题很久了。

在 Windows 上,没有任何 C++ 标准库以完全相同的方式占据主导地位,部分原因是 OS 供应商工具 (Microsoft Visual C++) 的每个版本都引入了不兼容性。是的,有些事情基本保持不变(名称修改),但其他事情发生了根本性的变化(分配器获得了低碎片堆支持,容器获得了调试迭代器,等等)。

具有讽刺意味的是,Windows 上的 ABI 标准化现在比 Linux1 上的要好,因为 Windows ABI 指定了一个C++ 特性 -- vtable 布局 -- 支持 COM。

原生 C++ 的底线是您可以在 DLL 中自由使用它,但是为了解耦,DLL 的 public 表面只需要使用 OS ABI 中指定的功能-- extern "C" 名称、C 兼容类型和(仅 Windows)抽象基础 classes 只包含虚函数(没有静态成员,没有非静态数据,所有非静态函数是虚拟的,构造函数是受保护的)。

但是如果你能有耦合,你会得到更多的特性。如果应用程序中的每个组件都同意 .NET ABI,那么它的所有功能都可用。 .NET 的 ABI 在开发时考虑到了向后和向前的兼容性,从 C++ 的不兼容性和以前的尝试中吸取教训,例如 Java.

因为 .NET ABI 非常稳定,与其耦合是一个可行的决定,就像许多 Linux 开发人员耦合到 C++11 之前的 stdlibc++ ABI 一样。但是如果你破坏了兼容性,你 运行 有失去所有可重用库的风险,这就是为什么耦合到任何特定的 Windows C++ 库被认为是一个坏主意——升级你的编译器需要你有新的每个库的版本。 当即使是一个组件卡在旧版本上也无法采用新的编译器时,您就会失去对已切换的其他组件的新开发。这就是为什么 Windows 上的 C++ ABI 耦合被广泛认为是一个非常糟糕的主意。以及为什么库作者很少以二进制形式分发 Linux 代码——他们让每个分发选择一个 ABI,始终如一地使用它进行编译,并提供与之兼容的二进制包。

当然,这只会解决来自源代码外部的更改(库类型的布局更改、参数传递顺序等)。如果您的源代码发生变化,例如通过添加新的函数参数或更改 class 类型中的数据成员,那么无论 ABI 多么好 (well-specified/stable),您都将无法兼容。 .NET 通过两阶段编译系统避免了其中一些影响——当依赖项发生变化时,整个世界 确实 得到重新编译,因为 JIT 编译是在 运行 时间完成的。但即使在 .NET 中,一些更改(如添加新的函数重载)在生效之前也需要重新编译源代码。


其实我不确定,vtable-layout 也可能在 Linux 中的 OS 级别指定。重要的是 Linux C++ 二进制兼容性不再通用,这让许多开发人员感到非常惊讶,他们实际上依赖编译器提供的事实上的标准 ABI,同时假设 OS ABI gua运行teed 永远具有普遍兼容性。因此,Windows 已经学会管理二进制兼容性不足的开发人员处于更好的位置。