LLVM 项目博客

LLVM 项目新闻和来自战壕的细节

C++11 迁移工具现状

设计文档 为 cpp11-migrate,C++11 迁移工具,首次提出 在 2012 年 12 月初,开发一直在稳步进展。在本文中,我将讨论 cpp11-migrate 中迄今为止已经实现的功能,即将推出的功能以及如何参与其中。

C++11 迁移工具的目的是进行源代码到源代码的转换,以将现有的 C++ 代码迁移到使用 C++11 功能,从而增强可维护性,可读性,运行时性能和编译时性能。开发仍处于早期阶段,转换主要属于前两个类别。迁移工具基于 Clang 的 LibToolingAST 匹配库

迄今为止,大部分开发工作都由英特尔的一个小型核心小组进行。到目前为止,我们的重点是建立项目基础架构和测试,实现一些基本的转换,并确保这些转换正常工作。我们的目标是使此工具对社区有用,因此我们一直在倾听转换想法和反馈。

如何获取 cpp11-migrate

cpp11-migrate 位于 Extra Clang Tools 存储库中。要构建 cpp11-migrate,您还需要 LLVM 和 Clang 源代码。按照 Clang 的 入门指南 中的说明进行操作,确保执行签出 Extra Clang Tools 存储库的可选步骤。一旦签出到正确的目录,构建系统在重新配置后,将自动将额外的 Clang 工具包含为下次完整构建的一部分。如果您使用的是 CMake 构建系统,则可以使用 cpp11-migrate 目标. 来构建 cpp11-migrate。CMake 提供的 check-clang-tools 目标将运行所有额外 Clang 工具的回归测试,包括 cpp11-migrate。

迄今为止的转换

C++11 迁移工具目前支持 C++11 的四个功能
  • 基于范围的 for 循环
  • 用于空指针的 nullptr 字面量
  • the auto 类型说明符
  • override 虚拟说明符
基于范围的 for 循环转换 曾经作为一个独立的工具存在,称为 loop-convert ,由 Sam Panzer 贡献。当开始开发更多转换时,这个想法变成了将所有转换都放在一个工具的管辖范围之内,并且 cpp11-migrate 就诞生了。基于范围的 for 循环转换将替换以下三种常见情况之一中使用的 for 循环:
  1. 使用迭代器遍历容器的循环
  2. std::vector<int> myVec;
    for (std::vector<int>::iterator I = myVec.begin(),
    E = myVec.end();
    I != E; ++I)
    llvm::outs() << *I;
    std::vector<int> myVec;
    for (auto & elem : myVec)
    llvm::outs() << elem;
  3. 遍历静态分配数组的循环
  4. int arr[] = {1,2,3,4,5};
    for (int i = 0; i < 5; ++i)
    llvm::outs() << arr[i];
    int arr[] = {1,2,3,4,5};
    for (auto & elem : arr)
    llvm::outs() << elem;
  5. 使用 operator[] 或 at() 遍历类数组容器的循环。
  6. std::vector<int> myVec;
    for (int i = 0; i < myVec.size(); ++i)
    llvm::outs() << v[i];
    std::vector<int> myVec;
    for (auto & elem : myVec)
    llvm::outs() << elem;
nullptr 转换 在将指针初始化为或分配为 null 值的地方使用新的 nullptr 字面量。在使用显式转换的情况下,会保留显式转换,以避免在代码中引入歧义。
void foo(int *arg);
void foo(float *arg);

int *IntPtr = 0;
float *FloatPtr = NULL;
foo(static_cast<int*>(0));
void foo(int *arg);
void foo(float *arg);

int *IntPtr = nullptr;
float *FloatPtr = nullptr;
foo(static_cast<int*>(nullptr));

auto 类型说明符转换 将变量声明的类型说明符替换为新的 auto 关键字。一般来说,只要变量声明的类型与其初始化程序的类型匹配,就可以进行这样的替换。但是,转换仅针对几个特定有用的情况,并着重考虑可读性和可维护性
  1. 当变量是 STL 容器的迭代器时。
  2. std::vector<std::pair<int, std::string> >::iterator NameAgeI = People.begin();
    for (std::vector<MyType>::iterator I = Container.begin(),
    E = Container.end;
    I != E; ++I) {
    // ...
    }
    auto NameAgeI = People.begin();
    for (auto I = Container.begin(),
    E = Container.end;
    I != E; ++I) {
    // ...
    }
  3. 当初始化程序是使用 new 运算符进行的分配时。
  4. MyType *VarPtr = new MyType();
    MyType * const VarCPtr = new MyType();
    auto VarPtr = new MyType();
    auto const VarCPtr = new MyType();
对第三种情况的支持正在开发中:使用工厂函数创建对象。
MyType *FooPtr = makeObject<MyType>(/*...*/);
MyType *BarPtr = MyType::create(/*...*/);
auto FooPtr = makeObject<MyType>(/*...*/);
auto BarPtr = MyType::create(/*...*/);
在每种情况下,声明变量的推断类型都应该对读者来说是显而易见的。标准容器的迭代器是通过具有特定名称的函数创建的,并且在特定情况下使用。对于工厂函数和运算符 new类型在初始化程序中拼写出来,因此在变量声明中重复它没有必要。

the override 虚拟说明符转换 ,由 Philip Dunstan 贡献,是迁移工具的第四个转换,也是第一个来自英特尔核心小组之外的贡献。此转换检测派生类中的虚拟成员函数,这些函数覆盖了父类中的成员函数,并将 override 虚拟说明符添加到函数中。
class Parent {
public:
virtual int getNumChildren();
};

class Child {
public:
virtual int getNumChildren();
};
class Parent {
public:
virtual int getNumChildren();
};

class Child {
public:
virtual int getNumChildren() override;
};

有关这些转换的更多详细信息,包括它们可以做什么和不能做什么,如何调整它们的行为以及已知的限制,可以在 cpp11-migrate 用户手册 中找到。

在真实项目上进行测试

还有什么比在整个真实项目上运行 C++11 迁移工具更好的测试方法呢?我们已经建立了一个持续集成服务器,到目前为止,该服务器已经在两个项目上构建并运行了 cpp11-migrate,并且计划至少再添加三个项目。对于每个项目,目标是构建转换后的代码并运行该项目的测试套件,以确保语义没有改变。
已实现
  1. LLVM 3.1
  2. ITK 4.3.1
计划
  1. LLDB
  2. OpenCV
  3. Poco
在真实代码上运行迁移工具对于查找错误非常有帮助。来自不同项目的真实代码通常会揭示在转换的开发和单元测试中未考虑到的代码表达式。每次从转换这些项目中发现的错误得到修复后,新的测试用例就会添加到回归测试套件中,并且迁移工具变得更加健壮。

未来工作

修复通过迁移真实代码发现的错误目前具有很高的优先级,因为我们希望尽可能快地为尽可能多的人提供良好的用户体验。添加更多转换是另一个优先事项,那些社区最感兴趣的转换将首先出现。目前列表中排名靠前的是
  1. 使用标准库而不是 TR1
  2. 替换对已弃用的 auto_ptr 类的使用。
除了修复错误和添加转换之外,还需要考虑更一般的改进。我们正在取得进展的一项改进是,取消仅转换源文件而不转换任何包含的头的限制。到目前为止,此限制一直存在,因为迁移工具需要知道哪些头文件是安全的转换。系统头文件和第三方库头文件显然不应该被修改。

参与进来!

如果您想参与,您可以做的第一件事就是在您的代码上试用 cpp11-migrate。可以通过 LLVM 的错误跟踪器 在产品 clang-tools-extra 下记录错误。如果您需要帮助或想更多地参与,请发送电子邮件到 Clang 开发者邮件列表。我们期待您的来信!