死亡日标准图书馆

在布拉格的前一天,C ++标准化委员会对更改ABI的问题进行了一系列调查,最终决定不对其进行任何更改。大厅里没有掌声。
我认为我们尚未完全意识到此决定带来的后果,而且我不认为原则上可以对语言的发展产生积极影响。



什么是ABI?


ABI-一组约定,用于定义程序以二进制形式表示,如何在其中声明名称,定义类标记以及定义函数调用约定。实际上,这是一种二进制协议,但未进行版本控制。
为了不使您对术语感到困惑,让我们看一下ABI更改带来的后果及其在程序中的细分示例:

如果您执行以下任何操作,则不能在新版本的编译库中使用导出的符号:

  • 在现有课程中添加了新字段
  • 更改类/函数的模板参数,将模板函数转换为非模板,反之亦然,添加可变参数模板
  • 内联说明符应用于某些内容
  • 在函数声明中添加了默认参数
  • 宣布了新的虚拟方法

还有很多,但是上面的例子是委员会提到的主要例子,也正是这些例子,由于这些例子,标准中的大多数提议都被当场销毁了。我省略了违反ABI的选项,在此期间,程序的源代码也会中断(例如,删除或更改功能)。但是,并非总是如此。例如,删除一个函数并不总是需要破坏源代码。std::string它在中有一个转换运算符std::string_view,我很高兴摆脱它,尽管删除它会破坏ABI,但不会在源代码中引起重大问题。

为什么要打破ABI


首先,如果破坏当前的ABI,可以对标准库的实现进行一些有用的更改:

  • 加快关联容器的速度
  • 加快您的工作速度std::regex(目前,运行PHP并使用正则表达式进行搜索要比使用标准表达式更快std::regex
  • 在某些变化std::stringstd::vector以及在其他容器的布局
  • 类接口的统一性:目前,出于稳定性考虑,它们的某些实现有意不对应于单个接口

更重要的是,库的设计中的更改必须在不遇到ABI安全问题的情况下进行。由于上述原因,这是目前不可行的所有清单,但不完整:

  • std::scoped_lock 被添加以不破坏abi更改 lock_guard
  • int128_t , intmax_t. , , , intmax_t deprecated
  • std::unique_ptr , zero-overhead
  • error_code - , ABI
  • status_code ABI
  • recursive_directory_iterator , ABI
  • <cstring> constexpr ( strlen) , ABI
  • UTF-8 std::regex — ABI
  • 添加支持realloc并返回已分配内存的大小是多态分配器的ABI分解
  • 为多态类型创建隐式虚拟析构函数
  • push_back如果当前的ABI损坏,则可以更改返回类型y
  • 通常,我们真的需要和push_back,以及emplace_back吗?
  • std::shared_ptr 也会导致ABI故障
  • [[no_unique_address]] 如果我们不关心保存当前的ABI,则可以由编译器输出

列表不止于此。我认为WG21应该努力保持此类清单的最新状态。但是我会注意到和我在一起的每个人都说“这将破坏ABI”。

我们还想改变什么?


我不知道。我不知道我不知道。但是,如果让我猜测,我会说以下内容:

  • C++23 ABI, - , ABI.
  • , , , , ABI
  • ABI,
  • , ABI
  • Tombstone ABI

ABI


在上一次ABI讨论中,在布拉格进行了一些调查,但是没有告诉我们任何事情。根据您是悲观主义者还是乐观主义者,您对当前结果的理解可能会有所不同。

关键事实:

  • WG21不想破坏23中的ABI
  • WG21认为有必要在将来的C ++版本(C ++ 26或更高版本)中破坏ABI。
  • WG21将花一些时间来考虑违反ABI的提案
  • WG21不保证永恒的稳定性
  • WG21认为重要的是保持性能优先,甚至损害语言稳定性

这些声明有许多要点,但尚未达成共识。奇怪的是,委员会一分为二。

算命


在哪个版本的C ++中等待更改


所有这些民意调查的明显缺点是,我们还没有决定何时确切打破ABI。

在C ++ 23中?不,绝对不会。

在C ++ 26中?有些人打算投票,但另一部分人可能会投票支持C ++ 41或退休之时,而他们不必支持当前的项目。刚才提到的问题之一是C ++- 一些;非常舒适!

没有理由相信,如果现在不能违反ABI,那么以后就可以违反。需要稳定性的人落后于标准数年。因此,如果我们现在不打破它,人们将继续依赖从未承诺过的ABI,也许还要十年甚至二十年。我们进行这项调查的简单事实最终被投票反对不违反ABI,这表明整个生态系统正在逐渐变得僵硬和停滞-每天问题只会变得更加严重,而且价格可能更高。

我认为三年来进行的调查不会改变任何事情。这就像全球变暖:每个人都同意,有一天我们需要解决这个问题。然后让我们在2070年禁止柴油吗?

计划在未来五年内完成的所有工作都可能永远不会发生。

关于违反ABI的报价


WG21投票决定将更多时间用于违反当前ABI的提案。这意味着几件事:

  • 我们将在委员会中噪音最大的会议室中浪费更多的时间来讨论此问题,而对那些有更多机会获得通过的提案则少花些时间,最后我们将全部拒绝。
  • 我们将寻找不会破坏ABI的替代方法(将在下面进行讨论)
  • 可能会引入ABI的部分更改(另请参见下文)

性能比ABI稳定性更重要


这就像问一个五岁的孩子是否想要糖果。当然,我们将对绩效的重要性进行投票。但是,我担心仍然有人投票反对。

在我看来,委员会希望同时坐在两个椅子上。这是不可能的:
Bryce Adelstein Lelbach @blebach-
性能
-稳定性ABI-
更改某些内容的能力

从建议的选项中选择两个选项。

语言的稳定性和ABI当然会相互冲突,从而迫使我们提出这样一个基本问题-什么是C ++,什么是它的标准库?

通常在这种情况下,记住术语“性能”,“ 零成本抽象”,“ 不为您不使用的产品付费 ”。而ABI的稳定性贯穿于所有这一切。

后果深远


图片

我深信,决定在23年内不打破ABI是委员会有史以来最大的错误。我知道有人相信相反的说法。但是,这可能很快就会发生:

学习的噩梦


说实话。所有依赖ABI的程序都可能在某处违反ODR原则或使用不兼容的标志,幸运的是,这些标志仍然有效。

新程序必须从源代码中编译,我们需要从源代码中构建程序集的工具,而不是从某处获得并以某种方式插入到项目中的库的集合。

是的,从源代码构建并不是一件容易的事情。但是我们需要鼓励对产品采用这种方法,定期更新编译器,以便人们在发行后一个月而不是十年后可以从新引入的功能中受益。需要鼓励使用正确,可靠,可扩展和可复制的解决方案,开源库和依赖系统。

该委员会拒绝违反ABI,公开声明它将在其存在的整个过程中支持写得不好的程序。即使您没有链接到通过apt-install获得的库(实际上是为系统使用的),也会有其他人,因为委员会给予了他们祝福。

这是一大步。如果我们没有动力去教别人良好的语言习惯?

对标准库失去兴趣


由于我们不愿意违反ABI而造成的图书馆性能损失估计为5-10%。这个数字只会随着时间增长。让我们看一些例子:

  • 如果您是大型公司,则可以为自己购买一个新的数据中心,或向支持自己的图书馆的程序员团队付费
  • , 5% ,
  • , 5-10% , VR-
  • , —

我认为,在这里,我会随意问:“我绝对应该使用C ++及其标准库!”和“也许我不应该使用标准库?还是我原则上不应该使用C ++?也许.NET,julia或Rust会是更好的解决方案?”当然,有很多因素会影响答案,但是我们看到了最近发生的事情。

许多游戏开发人员都对标准库持怀疑态度。他们宁愿开发自己的替代产品(例如EASTL),也不愿利用STL。 Facebook 愚蠢,谷歌abseil等等。

就像雪球一样。如果人们不使用标准库,他们就没有兴趣对其进行改进。性能是使磁带库保持运转的关键因素。没有性能的保证,其开发将花费更少的精力。
>> JonathanMüller @foonathan
如果不能提供更好的性能,那么使用标准库中的容器有什么意义呢?

泰特斯·温特斯 @TitusWinters
也许是因为它们很常见且易于访问?(这两个事实并不意味着同一件事)。
投票保护ABI就像在说标准库应该努力成为麦当劳-他也无处不在,他很稳定并且从技术上解决了任务。

委员会如何考虑打破ABI的提案?


由于无法接受要约而导致违反ABI,因此提供了多种选择来缓解疼痛:

添加新名称


图片

这是第一个显而易见的解决方案。如果我们不能改变std::unordered_map,我们可以补充std::fast_map吗?造成这种情况不好的原因有很多。在支持成本和教育方面,将类型添加到标准库都是昂贵的。引入新类后,将不可避免地出现数千篇文章,说明应使用哪个容器。例如,我应该使用std::scoped_lock还是std::lock_guard?我不知道!我每次都需要Google。还有一个问题,好名声迟早会终止。在程序执行过程中,我们还会遇到一些开销,因为所有容器必须不断地相互转换,因此很难控制类中的大量转换重载,等等。

具有讽刺意味的是,但支持上述解决方案的人也可能会认为C ++语言太复杂。将重复项添加到库中绝对不会使其变得更容易。

但是我们可以接受此优惠为标准!


一些图书馆开发商声称,他们的报价由于违反了ABI而被拒绝,尽管他们实际上并未违反任何内容,或者可以更改它们以规避ABI的失败。

作为一个愤世嫉俗的人,我很难相信。事实是,在没有此类建议之前,可以应用这些建议的方案非常有限。ABI审核小组(ARG)可以在此问题上提供帮助,但他们可能会再次为该类/功能推荐另一个名称。

局部违反Abi


主要思想不是破坏整个ABI,而只是针对特定的类或功能对其进行更改。这种方法的问题是,在链接阶段而不是错误的情况下,我们将在程序启动期间就已经看到了问题,这会让我们感到惊讶。该委员会在更改标记时已经在C ++ 11中尝试了这种方法std::string一切都不好。一切都太糟糕了,以至于这个事实仍然被用来支持维持当前的ABI。

另一个索引级别


解决ABI某些问题的方法是能够通过指针访问类的数据,然后类的标记就是该指针。这个想法非常接近PIMPL习惯用法因为它的ABI使其Qt中得到了积极的应用。是的,这可以解决类成员的问题,但是使用虚方法怎么办?

从更严格的角度考虑问题,我们正在讨论的是为ABI框架中包含的所有内容添加另一种间接级别(按指针索引)和在堆中额外分配内存。实际上,在STL中,所有内容都包含在此框架内,因为它是通用类的集合。

结果,这种方法的代价将是巨大的。

作为该问题的解决方案,标准中已经有一些建议。他们中的一些人希望使PIMPL成为语言的功能之一,因此您可以在ABI稳定性和高性能之间进行选择。

具有讽刺意味的是,但是,为了将库类型转换为PIPML类型,我们需要...打破ABI。

每三年重新汇编一次所有代码


大声说出我的想法。

标准中的所有当前报价必须被销毁


矛盾的是,C ++从未像现在这样活跃。在布拉格,有250人为他工作,包括:

  • 数值
  • 线性代数
  • 音讯
  • 统一码
  • 异步I / O
  • 2D和3D图形
  • 许多其他功能

所有这些建议都由一个共同的事实统一起来-与我们目前的标准相比,它们更具可选择性。人们只是试图从他们的研究和工作领域,或者是不断发展变化的领域中标准化事物。
尤其是,Unicode算法非常不稳定,并且会随着时间而迅速变化。

然后,这种恐怖的网络织机在地平线上。试图对可能导致安全问题的任何事情进行标准化是非常非常不负责任的,同时又无法在以后进行更改(记住有关ABI的信息)。

由于C ++决定使其稳定,因此所有这些建议都必须销毁并销毁。我不想被摧毁,但这必须做到。

但是他们仍然不会这样做。

在最佳情况下,我们不会在新版本的C ++中犯错误并标准化事物的当前状态,然后让所有事物缓慢分解,因为不可能对其进行修复(对于Networking TS,我们似乎根本无法进行任何更改,因此我们将不得不标准化十年前存在的内容,那么当然仍然可以对库进行重大改进,但是让我们再讲一次这个故事)。

但是,当然,我们会犯很多错误。

>> Ólafur Waage @olafurw
( , )

, !

. , ( : , , )?

Hyrum Wright @hyrumwright
, , . — , .

有些错误是权衡取舍,是有意为之,而其他错误很多年来却未引起注意。

时间流逝,但是标准库停滞不前。以前做出的取舍逐渐开始困扰我们,后来成为现有代码中真正的“瓶颈”。

有些事情实际上是无法更改的,因为它们已嵌入在API中。我们都有一个想法,即更改现有API有多困难。但是,如果我们可以破坏ABI,部分代码仍可以修复和改进。

在接下来的40年中,C ++仍将继续流行。如果我们无法意识到需要随时以不可预测的方式进行更改,那么原则上唯一的正确选择就是不要玩这个游戏。

每个人都知道标准的关联容器使用不到十年才有意义。但是,为什么我们认为标准中更大的提案会更成功呢?

您对标准的报价将被破坏,我的出价将被破坏。

委员会原则上可以打破ABI吗?


许多人确信委员会原则上不能做出这样的决定,因为那样的话,图书馆开发人员只会忽略它。所有这些痛苦地类似于手臂摔跤,委员会决定不参加比赛。

但是事实是,任何产品的开发人员都有自己的用户。用户是那些首先需要了解对他们施加哪些折衷的人。

许多人很偶然地依赖ABI,而没有做出明智的选择。许多人也依赖稳定,因为当然每个人都希望依靠稳定。但是,就像其他任何事情一样,稳定性是有代价的,现在整个C ++生态系统为此付出了代价。

All Articles