C++11 迁移工具现状
自 设计文档 为 cpp11-migrate,C++11 迁移工具,首次提出 在 2012 年 12 月初,开发一直在稳步进展。在本文中,我将讨论 cpp11-migrate 中迄今为止已经实现的功能,即将推出的功能以及如何参与其中。C++11 迁移工具的目的是进行源代码到源代码的转换,以将现有的 C++ 代码迁移到使用 C++11 功能,从而增强可维护性,可读性,运行时性能和编译时性能。开发仍处于早期阶段,转换主要属于前两个类别。迁移工具基于 Clang 的 LibTooling 和 AST 匹配库。
迄今为止,大部分开发工作都由英特尔的一个小型核心小组进行。到目前为止,我们的重点是建立项目基础架构和测试,实现一些基本的转换,并确保这些转换正常工作。我们的目标是使此工具对社区有用,因此我们一直在倾听转换想法和反馈。
如何获取 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 虚拟说明符
- 使用迭代器遍历容器的循环
- 遍历静态分配数组的循环
- 使用 operator[] 或 at() 遍历类数组容器的循环。
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;
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;
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;
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 关键字。一般来说,只要变量声明的类型与其初始化程序的类型匹配,就可以进行这样的替换。但是,转换仅针对几个特定有用的情况,并着重考虑可读性和可维护性
- 当变量是 STL 容器的迭代器时。
- 当初始化程序是使用 new 运算符进行的分配时。
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) {
// ...
}
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(/*...*/);
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,并且计划至少再添加三个项目。对于每个项目,目标是构建转换后的代码并运行该项目的测试套件,以确保语义没有改变。
在真实代码上运行迁移工具对于查找错误非常有帮助。来自不同项目的真实代码通常会揭示在转换的开发和单元测试中未考虑到的代码表达式。每次从转换这些项目中发现的错误得到修复后,新的测试用例就会添加到回归测试套件中,并且迁移工具变得更加健壮。未来工作
修复通过迁移真实代码发现的错误目前具有很高的优先级,因为我们希望尽可能快地为尽可能多的人提供良好的用户体验。添加更多转换是另一个优先事项,那些社区最感兴趣的转换将首先出现。目前列表中排名靠前的是
- 使用标准库而不是 TR1
- 替换对已弃用的 auto_ptr 类的使用。
参与进来!
如果您想参与,您可以做的第一件事就是在您的代码上试用 cpp11-migrate。可以通过 LLVM 的错误跟踪器 在产品 clang-tools-extra 下记录错误。如果您需要帮助或想更多地参与,请发送电子邮件到 Clang 开发者邮件列表。我们期待您的来信!