Non-Profit, International

Spirit unsterblich.

为什么不能分发C++二进制模块接口(BMI)

字数统计:562

原因:不同编译器,不同编译参数会导致内部的AST不一致。

AST不一致的来源有以下几个:

  1. 编译器提供的宏不一样,例如代码使用 __clang__ 或者 _MSC_VER 进行条件编译
  2. 有些库允许用户更改宏的定义以支持不同的功能,例如 NDEBUG 宏和 _UNICODE
  3. 不同C++标准下,第三方库和标准库提供不同的功能
  4. 实现定义的内容可以被编译器选项控制,例如 -fexecution-charset会改变基本字符串字面量的编码,-funsigned-char会改变 char 的符号性
  5. 编译器实现细节导致的定义不一致,例如不同编译器支持的intrinsic不一样,以及我为Clang实现的“糖类型”在AST可见但语言不可见(Clang内部有多个糖类型)
  6. 编译器支持的扩展不同,例如MSVC不支持 __int128_t,GCC把 __int128_t_Bitint(128) 实现为相同类型,而在Clang中是不同类型,如果使用 is_same_v,这也会改变代码的行为

实际上以上所有产生不同AST的行为,在被ODR使用时(见下文)都违反ODR,这不是模块的缺点,使用头文件时存在上述任何问题仍然有可能产生不同AST并且违反ODR。

因此,分发BMI显然是天方夜谭,我不知道什么人在宣传BMI能分发或者生成BMI不能分发是某种缺点,他们显然对编译C++一窍不通。

在不使用模块时,不同翻译单元对于“同一定义”有不同的AST不一定违反ODR,因为它们不一定被ODR使用。

例如 __is_scoped_enum 这个Clang的intrinsic一定被常量求值,在多个翻译单元中它的值一定是相同的。使用模块后,模块会把 __is_scoped_enum 保留在AST里,被依赖它的模块使用(合并),此时,这个BMI就不能被不支持 __is_scoped_enum 的编译器(例如MSVC和EDG)使用。


若无特殊声明,本人原创文章以 CC BY-SA 4.0许可协议 提供。