CppCon 2018 学习:TEACHING OLD COMPILERS NEW TRICKS TEACHING OLD COMPILERS NEW TRICKS Transpiling C++17

理解问题与解决方案:C++17 和 编译器兼容性

这个问题描述了由于编译器版本差异(特别是 GCC 4.9 与 GCC 4.8),在不同编译器版本下生成的汇编代码可能会有所不同,尤其是 C++17 中的某些新特性和语法在老版本的编译器中可能无法正确处理。这种情况通常会导致程序行为不一致,特别是在对性能或低级代码有要求时。

具体问题:

问题描述:
  1. C++17 特性与 GCC 4.9:
    • 例如,0b1000'0000 是 C++17 中引入的二进制字面量表示法, GCC 4.9 对此特性处理是没有问题的,能够生成正确的汇编代码。
    • 汇编代码生成:
      mov eax, 128
      ret
      
  2. C++17 特性与 GCC 4.8:
    • 但是 GCC 4.8 无法正确处理这个二进制字面量(甚至可能根本不支持 C++17 的一些新特性)。因此,即使使用了 C++17 语法,GCC 4.8 生成的汇编代码可能没有正确的优化或存在其他问题。
    • 汇编代码生成:
      mov eax, 128
      ret
      
影响:
  • 编译器版本差异会导致代码生成的差异,在某些情况下可能会影响程序的正确性、性能,甚至导致程序崩溃。
  • 选择较旧的编译器版本(例如 GCC 4.8)可能会导致更多的维护成本和潜在的错误风险,特别是对旧版 C++ 标准的支持可能不完全。

解决方案:

  1. 升级到更新的编译器:
    • 最简单的解决方案是升级到支持 C++17 或更高标准的编译器(例如 GCC 4.9 或更高版本)。这样可以确保对现代 C++ 特性的全面支持,包括新的字面量表示法、库和语法等。
    • 然而,在某些生产环境中,升级编译器可能不可行,特别是对于一些较老的代码库。
  2. 使用旧版 C++ 标准:
    • 如果无法升级编译器,可以选择回退到较旧的 C++ 标准(如 C++11 或 C++14),这些标准通常与较旧版本的编译器更兼容。
    • 这可能会增加维护成本,因为一些现代特性(如 C++17 中的二进制字面量)将无法使用。
  3. 使用 Clang-from-the-Future:
    • Clang-from-the-Future 是一个新的基于 libclang 的工具,能够通过预处理器自动将 C++17 代码转换为 C++11。这意味着你可以继续使用现代的 C++17 代码,而编译器会将其转化为兼容旧版编译器的代码。
    • 这种方法允许开发者在较旧的编译器上构建和运行 C++17 代码,避免了版本不兼容的问题。

总结:

  • 问题: 由于编译器版本差异,使用 C++17 时会遇到一些特性在老编译器(如 GCC 4.8)中不被支持,导致生成的汇编代码不一致。
  • 解决方案:
    1. 升级编译器至支持 C++17 的版本。
    2. 使用旧版本的 C++ 标准(如 C++11 或 C++14)以兼容旧编译器,但会增加维护成本。
    3. 使用工具如 Clang-from-the-Future,自动将 C++17 代码转换为兼容 C++11 的代码,从而在旧编译器中使用现代 C++ 特性。

理解 CFTF Enhanced Pipeline 和其解决方案:

这部分描述了 CFTF (Clang-from-the-Future) 增强的构建流程,以及它如何解决在较旧编译器上使用现代 C++ 特性的问题,特别是 C++17 在旧版 GCC 4.8 编译器下无法正常工作的情况。

概念解析:

CFTF Enhanced Pipeline:
  1. 源代码 → 构建 AST → 转换成 C++11 源代码 → CFTF 预编译(黑盒预处理步骤)
    • 在这个流程中,CFTF 执行了一个“黑盒预编译”步骤。即它会将 C++17 源代码转化为 C++11 代码,使其能够与旧版编译器(如 GCC 4.8)兼容。
  2. CFTF Step:
    • 通过这种方式,开发者可以继续使用现代 C++17 特性,而不需要担心编译器不兼容问题。
    • 这避免了在老旧编译器上编译失败或产生错误汇编代码的问题。
    • 编译器会看到符合 C++11 标准的代码,而这些代码能够与旧版本的编译器兼容。
传统编译流水线与 CFTF 流水线对比:
  1. 传统编译流水线
    • 源代码 → 通过 C++ 编译器 直接进行编译 → 通过 链接器 生成可执行文件 → 执行
  2. CFTF 增强编译流水线
    • 源代码 → 转换为 C++11 源代码 通过 CFTF 进行预编译 → 使用 C++ 编译器 进行编译 → 通过 链接器 生成可执行文件 → 执行
      这里的 CFTF 步骤是关键,它负责自动将 C++17 代码转换为 C++11 代码,从而使其能够与老旧编译器兼容。

CFTF 示例:

假设我们有以下代码:

int get_mask() {
  return 0b1000'0000; // 使用C++17中的二进制字面量
}
  • C++17 + GCC 4.9:能够正确地处理和生成汇编代码。
    get_mask:
        mov eax, 128
        ret
    
  • C++17 + GCC 4.8:无法处理二进制字面量,可能会产生错误的汇编代码,甚至无法编译通过。
    get_mask:
        mov eax, 128
        ret
    
  • CFTF 处理后的 C++11
    CFTF 将原始 C++17 代码转换为等效的 C++11 代码,然后 GCC 4.8 能够编译:
    int get_mask() {
      return 128;
    }
    
    汇编代码生成:
    get_mask:
        mov eax, 128
        ret
    

总结:

  • CFTF (Clang-from-the-Future) 是一个非常有用的工具,它通过将 C++17 代码转换为 C++11 代码,解决了旧编译器(如 GCC 4.8)不支持现代 C++ 特性的问题。
  • 它通过增强的预编译步骤,确保开发者能够继续使用新的语言特性,而不必担心编译器的版本问题。
  • 在实际使用中,CFTF 使得我们能够在较老的编译器上编译和运行现代的 C++ 代码,避免了因编译器版本差异带来的兼容性问题。
    这种方法对于想要在较旧编译器中保持现代 C++ 代码兼容性的项目尤其重要,尤其是在无法升级编译器的环境中。

CFTF (Clang-from-the-Future) 组件与功能

CFTF 是一个用于解决旧编译器无法支持现代 C++ 特性的工具,通过将 C++17 代码转换为 C++11 代码,从而实现兼容性。它具有以下几个关键组件和特点:

关键组件

  1. 命令行界面 (CLI):
    • 提供了简便的命令行工具,方便用户在构建过程中使用 CFTF 进行预编译。
  2. AST 访问器 (via libclang):
    • 使用 libclang 来访问并处理抽象语法树 (AST),为代码分析和重写提供支持。
  3. 重写引擎:
    • 负责将 C++17 代码转换成 C++11 代码,确保旧编译器能够正确编译和执行。
  4. 模板专用化器:
    • 特别支持模板编程,确保模板相关的语言特性能够在老旧编译器中正常工作。
  5. 测试套件:
    • 提供一套测试用例,以确保转换过程中的正确性,并验证不同 C++ 特性的支持。

功能示例

  1. If-init:
    • 使用 if 语句初始化变量。
  2. 结构化绑定 (Structured bindings):
    • 支持现代 C++ 中的结构化绑定(例如 auto [a, b] = get_pair();)语法。
  3. 自动返回类型推导 (Auto return type deduction):
    • 支持自动推导函数返回类型。
  4. If-constexpr:
    • 支持在 constexpr 环境下的 if 语句。
  5. 折叠表达式 (Fold expressions):
    • 支持现代 C++ 中的折叠表达式,简化变长模板参数包的处理。

实践中的应用

CFTF 方便集成到现有的构建管道中,下面是常见的两种构建工具的配置方法:

  1. Make:
    cftf -frontend-compiler=/usr/bin/g++ input.cpp
    
  2. CMake:
    CXX=/usr/local/bin/cftf CXX_FLAGS="-frontend-compiler=/usr/bin/g++" make
    CXX=/usr/local/bin/cftf cmake -DCMAKE_CXX_FLAGS="-frontend-compiler=/usr/bin/g++"
    
    其他构建系统可能需要一些创造性的解决方法来集成。

典型使用场景

  1. 新标准的早期采用与评估:
    • 在旧版编译器上尝试新标准的特性,比如 C++17 或 C++20。
  2. 在 C++11 设置中使用 C++17 库:
    • 使得开发者可以在不升级整个编译器的情况下,使用新的 C++17 库特性。
  3. 移植到遗留平台:
    • 在老旧的平台上使用现代 C++ 特性时,CFTF 提供了兼容的解决方案。
  4. 查看编译器看到的代码:
    • 通过 CFTF,开发者可以查看编译器实际处理的代码,以帮助调试和分析。
      如果你对这个功能感兴趣,可以参考 C++ Insights,它也提供了查看编译器行为的功能。

当前状态

  • 已发布:CFTF 已经在 Linux 系统中作为一个可用的工具发布,并支持与 GCCClang 配合使用。
  • Windows/macOS 支持:Windows 和 macOS 的补丁正在开发中,欢迎社区贡献。
  • 初期特性支持:目前只支持一小部分 C++14/17 特性,首要目标是确保正确性,然后逐步增加更多特性。

未来计划

  1. 更多重写规则
    • CFTF 会不断扩展支持的 C++ 特性,特别是 C++2a(即 C++20)特性,如合约(contracts)、概念(concepts)等。
  2. C++03 输出支持
    • CFTF 计划支持将 C++17 代码输出为 C++03 代码,包括移动语义、初始化列表等特性。
  3. 更好的测试覆盖率
    • 包括支持一些流行的库,如 hanarange-v3 等。
  4. 完全支持 C 预处理器
    • 提供全面的 C 预处理器支持,确保兼容性。
  5. 更好的调试体验
    • 提升调试工具,使得开发者能够更方便地调试和分析 CFTF 转换过程。

总结

CFTF 是一个非常有用的工具,能够帮助开发者在旧版编译器上使用现代 C++ 特性,它通过将 C++17 代码转换为 C++11 代码,解决了许多编译器版本不兼容的问题。该工具易于集成到现有的构建系统中,无需修改源代码即可使老旧编译器支持新特性。虽然目前处于初期阶段,但它已经具备了强大的功能,并且随着社区的支持和开发,未来会支持更多的特性和平台。
如果你需要在遗留平台上继续使用现代 C++,CFTF 无疑是一个值得尝试的工具!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值