使代码易于管理的方法之一是加强代码一致性,让任何程序员都可以快速读懂你的代码这点非常重要。保持统一编程风格并遵守约定意味着可以很容易根据“模式匹配”规则来推断各种标识符的含义。创建通用、必需的习惯用语和模式可以使代码更容易理解。虽然在一些情况下可能有充分的理由改变某些编程风格, 但我们还是应该遵循一致性原则,避免代码审查或者任务交接出现不必要的麻烦。
对于编程风格的统一,不仅仅是中小公司面临的棘手问题,对于google这样的大公司,同样存在这样的问题,google的开源项目大多都是用C++开发,正如每个C++程序员都知道的, C++有很多强大的特性,但这种强大不可避免的导致它走向复杂,使代码更容易产生bug,难以阅读和维护。
Google经常会发布一些开源项目,意味着会接受来自其他代码贡献者的代码。但是如果代码贡献者的编程风格与 Google 的不一致,会给代码阅读者和其他代码提交这造成不小的困扰。Google 因此发布了一份自己的编程风格,使所有提交代码的人都能获知 Google 的编程风格。这份规范不仅仅包括了C++风格指南,也包含了C、python、shell和Java。创新工场董事长兼CEO李开复曾经对Google C++编码规范给予了极高的评价:“我认为这是地球上最好的一份C++编程规范,没有之一,建议广大国内外IT研究使用”。
针对C++部分,其主要通过12个章节来约定格式规范,如图1所示,里面针对C++的头文件、类、函数的书写格式做了详细的说明,下面我挑选几个规范做一个介绍。
图1. C++风格指南目录
之所以不建议使用前置声明而使用“#include”,是因为前置声明隐藏了依赖关系,当头文件改动时,用户代码会跳过编译过程。极端情况下,用前置声明代替“#include”甚至都会暗暗地改变代码的含义,如图2所示:
图2. 前置声明示例
如果 #include 被 B 和 D 的前置声明替代,test()函数中,本意是希望形参x可以隐形转化为B然后调用f(B*),但是由于有f(void*)同名函数,导致test会调用 f(void*),偏离本意。
我们使用空格缩进,不要在代码中使用制符表,你应该设置编辑器将制符表转为空格,因为不同的代码IDE中,TAB代表的空格长度不一致,如果更换编译器后,就会导致对齐混乱。这一点在嵌入式开发过程中深有体会,我们写程序一般用vs studio或vs code,编译的时候使用IAR,经常会遇到在vs IDE里面缩进都没有问题,但是在IAR中,如图2所示,缩进会出现混乱的情景,就是因为IAR中TAB代表的是4个空格长度,而vs中普遍为2个空格长度。
图2. Vs code与IAR缩进对比
针对书写格式,c++编程指南也做了一些规范,比如在每个文件前,必须要有版权说明;在代码注释的时候,“//”与注释代码间保留一个空格符;书写函数时,大括号“{”不能另起一行,而是与函数名字间隔一个空格并且保持在同一行;代码块最后的空行要删掉……,有人总结了如下的一张图来说明该指南的规范要求,基本涵盖了所有的格式要求,如图3所示。
图3. C++格式指南说明
可见这份规范对C++的代码格式规定很详细,我只是简单列举了三条作为引入,具体感兴趣的同学可以在\\rd\软件部\Shared\工具软件\cpplint路径下查看具体规范。
[if !supportLists]2. [endif]Cpplint工具引入
有了上面约定好的一系列编码规范,就可以使我们团队每个成员的代码格式保持一致吗?答案当然是否定的,当然还需要再日常编程过程中去逐渐规范并慢慢培养成习惯,但是如果没有有效的控制行为,依靠人力去检查和维护这些格式,无疑是一个非常繁琐和复杂的工作,为此google开发了自己的C++代码规范检查工具—cpplint。cpplint是一个Python脚本,作为一款开源免费的代码静态检测工具,Google也使用它作为自己的C++代码检测工具,也就是说,只要你想代码遵从Google C++代码规范,那么Cpplint将会提供很好的代码静态检测支持。Cpplint的github仓库可以通过cpplint.git克隆到本地。
cpplint由于是python脚本,所以使用起来很简单,直接在命令行通过“cpplint.py {*.cpp}”即可,针对发现的每一个问题,都会给出一个位于区间[1, 5]之间的置信度评分, 分数越高就代表问题越肯定。你可以通过verbose选项控制输出哪些级别,如果代码中有些部分不希望被检查,或者你认为cpplint产生了误报,只需要在行尾添加注释 ‘// NOLINT’, cpplint就会跳过这些行. 如果你想过滤掉特定的警告, 就需要设置filter选项了(-表示不输出, +表示输出), 比如写一个测试文件:
图4. Cpplint 测试程序
打开命令提示符,如图5所示,会发现一共同拥有9个错误,我们需要按照提示信息,将格式修改正确后就可以了。
图5. Cpplint检查到的格式错误问题
虽然可以通过脚本文件对我们的代码进行检查,但是在实际项目中,我们不可能开发完代码之后将代码单独使用cpplint检查一遍,这样不仅繁琐,而且会耗费大量时间去处理。我这里提供三种方案:
在jenkins中配置静态检查工具,每次有gitlab merge request 时,自动触发代码静态检查工具,然后进行代码审查,这种方法适用于项目中,所有提交merge request的开发者。每个人提交到gitlab中的代码必须经过cpplint检查才能符合顺利合并的Dev分支。这种方式需要有gitlab权限的开发人员在服务器部署。
使用vs code或vs studio集成cpplint插件,每当保存完代码文件后,cpplint则会自动运行插件,以vs code 为例,每当文档发生改变自动保存后,就会用波浪线提示不符合格式规范的代码,同时在终端显示提示信息。只需要在安装cpp-check-lint插件即可。这种方式适合程序员提前预知不符合格式规范的代码,确保在提交前进行自检。
图6.vs code中格式警告信息
在每个开发者的本地,通过git pre-commit 钩子来进行适配。利用钩子检查commit的文件,如果提交的代码不符合cpplint的要求,则不被允许提交,相比于在jenkins中配置静态检查工具,每个人可在本地灵活配置cpplint的配置文件,用来设置不同的过滤等级和测试项,这种方式比较适合每个人在本地测试自己的代码风格,并做一些测试,下面我们主要介绍这种方式。
在每个git托管的项目下,都有一个.git的隐藏目录,里面的hooks文件夹默认配置了很多钩子,只不过默认情况下这些钩子函数是不可用的,如果要启用,需要将.sample取消,然后将以下内容拷贝到pre-commit中,并且在第5行配置正确的cpplint路径。
通过以上钩子,可以在每次提交代码时,对所提交的代码文件进行格式检查,如果没有检测通过,则不能被提交到本地仓库,并且也会说明不符合规范的提示信息,如图7所示
图7.git hooks的提交警告
总结
以上介绍了Google 公司的c++编码规范,他系统的规定了我们书写C++的日常规范,同时配合cpplint这样强大的自动检查工具,为他在日常代码编写规范中提供了切实的落地可能。同时介绍了三种在实际项目中,该工具的配置方法,实际在项目中,三种方法并不是冲突的,可以同时配合使用,提高团队成员开发效率。
当然这个工具也有一定的局限性,由于该规范是以文件为单位进行代码检查,并且目前笔者所在T04项目处于尾期,软件功能已完成90%以上,每次修复一个局部bug,都会检查其他代码的规范,如果严格限制格式,就需要将该文件所有代码重新修改,这样给cpplint的推广也带来了巨大的挑战,但是这对公司后期以C++为语言为主的新项目规范还是有一定帮助的。
一个好的规范是需要大家一起去遵守和维护的,规范逐渐地变成了一种习惯才能提高团队的开发效率,享受规范带来的便捷,就像十字路口的红绿灯,虽然从个体出发,会降低通行效率,但是从十字路口的利用率而言,是提高了每个个体的通行效率和路口的利用效率的。
参考文献
[1] https://github.com/cpplint/cpplint.