自动现代化C++代码

虽然C++11标准出来已经有好些年了,但是由于历史的原因,现在大部分C++项目仍然是C++03的语法。那么有没方法能够自动地把老的C++03代码替换成C++11代码?从而让我们享受到C++11新特性,像for-range loop,auto,nullptr,override等。

答案当然有的——clang-tidy。clang-tidy提供一系列的modernize-*checks。这些checks就是用C++11改写C++03。具体有下面这些:

  • modernize-avoid-bind: 使用lambda替换std::binding
  • modernize-deprecated-headers: 将C标准库的头文件include替换成C++style,#include <assert.h> => #include <cassert>
  • modernize-loop-convert: 使用for-range loop替换for(...;...;...;), 并更新for语句的相关变量。
  • modernize-make-shared: 找出所有显式创建std::shared_ptr变量的表达式,并使用make_shared替换。
  • modernize-make-unique: 跟make-shared一样,使用std::make_unique替换所有std::unique_ptr显式创建表达式。
  • modernize-pass-by-value: 在构造函数中使用move语义
  • modernize-raw-string-literal: 用C++11的raw string literal(R"...")替换原来的string literal, 这样的好处就是不用再添加转义符\了。
  • modernize-redundant-void-arg: 去掉void函数参数。
  • modernize-replace-auto-ptr: 用std::unique_ptr替换std::shared_ptr, std::shared_ptr是不推荐使用的,即使在C++98。
  • modernize-shrink-to-fit: 在C++03中,如果我们想修改STL容器的capacity,只能通过copy & swap的方式,C++11提供了shink_to_fit的方法。
  • modernize-use-auto: 在变量定义的时候,使用auto代替显式的类型声明,这个在定义STL容器类的Iterator特别方便。
  • modernize-use-bool-literals: 找出所有隐式从int转成boolliteral, 使用true或者false代替。
  • modernize-use-default: 对于没有任何自定义行为(定义为{})的特殊的成员函数,构造函数,析构函数,移动/复制构造函数,用=default代替掉{}
  • modernize-use-emplace: 使用STL容器中的emplace代替push_back
  • modernize-use-equals-delete: 在C++98中,类设计为了实现禁止调用某些特殊的成员函数,通常把它们声明成private;在C++11中,只需要在声明中体检=delete,找出所有private的特殊成员函数,并将它们标记成=delete
  • modernize-use-nullptr: 用nullptr代替NULL
  • modernize-use-override: 对于子类改写父类的virtual方法,在方法后面添加override, 并删掉virtual前缀,即virtual void NewOveride() => void NewOverride() override {}
  • modernize-use-using: 用using代替typedef, 如typedef int V => using V = int

如何应用到项目中

这里将以GitHub的electron开源项目为例子,如何应用clang-tidy来改写它的C++03代码:

  • 你需要导出项目的compilation database, 通常命名为compile_commands.json, 因为clang-tidy作为一个clang-based工具,需要知道如何编译每一个源文件(从compile_commands.json查找). ninja提供这个导出功能,只需要简单执行下面命令。对于其它不用ninja编译的项目,也是有工具导出的,方法请查看前一篇介绍clang-tidy文章
cd path/to/electron
# Make sure you can build electron successfully.
./script/build.py -c D
# Dump compilation database.
ninja -C out/D -t compdb cc cxx > compile_commands.json
  • 现在,我们可以尝试对项目中某个文件运行modernize-use-nullptr check, 我们需要添加-fix的命令参数,clang-tidy才会执行对原文件修改, 不然结果会定向到标准输出stdout
# Diagnose any `NULL` usage.
clang-tidy -checks="-*,modernize-use-nullptr" atom/browser/api/atom_api_menu.cc
# Replace all NULL usages to C++11 nullptr.
clang-tidy -checks="-*,modernize-use-nullptr" -fix atom/browser/api/atom_api_menu.cc
  • 我们当然不需要每次都手动执行一个源文件,run_clang_tidy.py脚本能够对每个compilation database中的每个文件都运行clang-tidy(使用多进程方法)。
# Run `modernize-use-auto` on all files in atom/* and apply fixes.
path/to/run_clang_tidy.py -checks="-*,modernize-use-auto" -fix atom/*

真实运行的结果,请查看electron#6423,已经被merge进upstream了。