C++最佳实践 | 1. 工具

420次阅读  |  发布于2年以前

本系列是开源书C++ Best Practises[1]的中文版,全书从工具、代码风格、安全性、可维护性、可移植性、多线程、性能、正确性等角度全面介绍了现代C++项目的最佳实践。本文是该系列的第一篇。

C++最佳实践:

1. 工具(本文)

2. 代码风格

3. 安全性

4. 可维护性

5. 可移植性及多线程

6. 性能

7. 正确性和脚本

前言

C++最佳实践: 支持Fork的编码标准文档

本文档旨在收集对C++最佳实践所进行的协作性讨论,是《Effective C++》(Meyers)《C++ Coding Standards》(Alexandrescu, Sutter) 等书籍的补充。在讨论如何确保整体代码质量的同时,补充了一些没有讨论到的较低级别的细节,并提供了具体的风格建议。

在任何情况下,简单明了都是首选。本文所举示例是为了说明为什么一种选择比另一种更受欢迎。在必要情况下,也会用文字说明。

本文档由Jason Turner编写,根据知识共享署名-非商业4.0国际许可协议[2]授权。

免责声明

本文档的编写基于个人经验,你不需要完全同意其中的观点。本文档保存于GitHub[3]上,任何人都可以fork供自己使用,或者提交修改建议与大家分享。

本文档启发O'Reilly发布了视频: Learning C++ Best Practices[4]

工具

应该在开发过程的早期建立用于执行这些工具的自动化框架,检出源代码、构建和执行测试所使用的命令不应超过2-3个,一旦测试完成,应该对代码的状态和质量有接近完整的了解。

源码管理

对于任何软件开发项目来说,源码管理都是绝对必要的,如果还没有,那就开始使用。

构建工具

使用广泛接受的行业标准构建工具,可以防止在做探索、链接新库、打包产品等等工作时重复发明轮子。例子包括:

请记住,这不仅是构建工具,也是编程语言。请尽量维护良好整洁的构建脚本,并遵循正在使用的工具的推荐实践。

包管理器

包管理是C++的重要主题,目前还没有明确的赢家。请考虑使用包管理器来帮助跟踪项目的依赖关系,从而帮助新人更容易开始参与项目。

持续集成

选择了构建工具之后,接下来需要设置持续集成环境。

在更改被推送到存储库时会触发持续集成(CI)工具自动构建源代码,可以私有部署CI工具或使用托管的CI系统。

如果在GitHub上有开源、公开托管的项目:

这些工具都是免费的,设置起来也相对容易。一旦把它们都设置好,就可以对项目进行持续的构建、测试、分析和报告,并且免费。

编译器

启用所有可用、合理的告警选项,有些告警选项只在启用了优化的情况下才有效,或者优化级别越高,效果越好,例如GCC中的-Wnull-dereference

应该使用尽可能多的编译器,每个编译器对标准的实现略有不同,支持多个编译器将有助于确保实现最可移植、最可靠的代码。

GCC / Clang

-Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic

考虑使用-Weverything,并且只在需要的情况下禁用少数警告。

-Weffc++警告模式可能太吵了,但如果对项目适用,也可以使用。

MSVC

/permissive- —— 执行标准一致性[38]

/W4 /w14640 —— 使用并考虑以下内容(参见下面的描述)

不建议

通用

一开始就设置非常严格的警告,在项目开始后试图提高警告级别可能会很痛苦。

考虑使用将警告视为错误的设置,例如MSVC中的/Wx,以及GCC/Clang中的-Werror

基于LLVM的工具

基于LLVM的工具与能够输出编译命令数据库的构建系统(例如cmake)配合得最好,例如:

$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .

如果没用这样的构建系统,可以考虑Build EAR[39],它可以与现有构建系统挂钩,并生成编译命令数据库。

CMake现在也提供了在正常编译期间调用clang-tidy[40]的内置支持。

静态检查

最好的选择是将静态分析器作为自动化构建系统的一部分运行,cppcheck和clang可以满足免费选项的要求。

Coverity Scan

Coverity[47]提供免费(开源)静态分析工具包,可以用于与Travis CI[48]和AppVeyor[49]集成的每个提交。

PVS-Studio

PVS-Studio[50]是用于检测用C、C++和C#编写的程序源代码中的bug的工具,对个人学术项目、开源非商业项目和个人开发者的独立项目都是免费的,可以在Windows和Linux环境下工作。

Cppcheck

Cppcheck[51]是免费、开源的。它努力争取零误报,并且做得很好。因此,应该启用所有警告: --enable=all

备注:

cppclean

cppclean[52]是开源静态分析器,专注于发现C++源代码中导致大型代码库开发缓慢的问题。

CppDepend

CppDepend[53]通过分析和可视化代码依赖关系、定义设计规则、进行影响分析以及比较不同版本的代码,简化了对复杂C/C++代码库的管理,对开源贡献者是免费的。

Clang的静态分析器

Clang的分析程序的默认选项适用于各个平台,可以直接通过CMake使用[54],也可以通过基于llvm的工具[55]中的clang-checkclang-tidy调用。

此外,CodeChecker[56]可以作为clang的静态分析前端。

clang-tidy可以通过Clang Power Tools[57]扩展轻松的和Visual Studio一起使用。

MSVC的静态分析器

可以通过/analyze命令行选项[58]启用,可以使用默认选项。

Flint / Flint++

Flint[59]和Flint++[60]是根据Facebook编码标准分析C++代码的linter。

OCLint

OCLint[61]是免费、自由、开源的静态代码分析工具,可以通过许多不同的方式提高C++代码的质量。

ReSharper C++ / CLion

这两种来自JetBrains[62]的工具都提供了一定程度的静态分析和自动修复功能,为开源项目负责人提供了免费许可证选项。

Cevelop

基于Eclipse的Cevelop[63] IDE提供了各种静态分析和重构/代码修复工具。例如,可以用C++的constexprs替换宏,重构命名空间(提取/内联using,限定名称),并将代码重构为C++11的统一初始化语法。Cevelop是免费的。

Qt Creator

Qt Creator可以插入clang静态分析器。

clazy

clazy[64]是基于clang的分析Qt使用情况的工具。

IKOS

IKOS[65]是开源静态分析器,由NASA开发。它以抽象解释为基础,用C++编写,使用LLVM为C和C++提供了分析器。源代码可以在Github[66]上找到。

运行时检查

代码覆盖率分析

覆盖率分析工具应该在测试执行时运行,以确保整个应用程序都被测到。不幸的是,覆盖率分析需要禁用编译器优化,这将导致测试执行时间大大延长。

Valgrind

Valgrind[73]是运行时代码分析器,可以检测内存泄漏、竞争条件和其他相关问题,支持各种Unix平台。

Dr Memory

和Valgrind类似。http://www.drmemory.org

GCC / Clang Sanitizers

这些工具提供了许多与Valgrind相同的特性,但内置在编译器中,易于使用,并提供问题报告。

注意可用的sanitizer选项,包括运行时选项。https://kristerw.blogspot.com/2018/06/useful-gcc-address-sanitizer-checks-not.html

Fuzzy分析器

如果项目接受用户定义的输入,可以考虑运行模糊输入测试。

这些工具都使用覆盖率报告来寻找新的代码执行路径,并尝试为代码提供新的输入。它们可以发现崩溃、挂起以及一些没有被考虑到的输入。

变异测试

这些工具获取在单元测试运行期间执行的代码,并改变执行的代码。如果测试在有突变的情况下仍然通过,那可能意味着在测试套件中存在有缺陷的测试。

控制流保护

MSVC的[控制流保护(Control Flow Guard)](https://msdn.microsoft.com/en-us/library/windows/desktop/mt637065%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 "控制流保护(Control Flow Guard "控制流保护(Control Flow Guard)")")增加了高性能的运行时安全检查。

检查STL实现
堆分析
忽略警告

如果团队一致认为编译器或分析器对不正确或不可避免的错误发出警告,则团队需要尽可能只在最小的范围内禁用特定的错误警告。

在对一段代码禁用该警告后,请确保重新启用该警告,没人希望禁用的警告被泄露到其他代码中[82]。

测试

上面提到的CMake有一个用于执行测试的内置框架,请确保使用的任何构建系统都能够执行内置测试。

为了进一步帮助执行测试,请考虑使用某个单元测试库,如Google Test[83]、Catch[84]、CppUTest[85]或Boost.Test[86],以帮助组织测试。

单元测试

单元测试针对的是可以独立测试的小代码块和独立功能。

集成测试

对于提交的每个特性或bug修复,都应该启用测试。参见上文介绍的代码覆盖率分析。这些测试比单元测试级别更高,但仍然应该被限制在单个特性的范围内。

逆向测试

不要忘记确保测试代码中的错误处理,并且确保其能够正常工作。如果目标是100%的代码覆盖率,很明显这些错误场景也需要被覆盖的。

调试

uftrace

uftrace[87]可以用来生成程序执行的函数调用图。

rr

rr[88]是一个免费、开源的反向调试器,支持C++。

其他工具

Lizard

Lizard[89]提供了针对C++代码库运行复杂性分析的非常简单的接口。

Metrix++

Metrix++[90]可以识别并报告代码中最复杂的部分,从而帮助我们减少复杂代码,帮助编译器更好的理解和优化代码。

ABI Compliance Checker

ABI Compliance Checker[91] (ACC)可以分析两个库版本,并生成关于API和C++ ABI变化的详细兼容性报告,可以帮助库开发人员发现无意的破坏性更改,以确保向后兼容性。

CNCC

Customizable Naming Convention Checker[92](可自定义的命名约定检查器)可以报告代码中不遵循特定命名约定的标识符。

ClangFormat

ClangFormat[93]可以自动检查并纠正代码格式,以匹配组织约定。可以参考关于clang-format的系列文章[94]。

SourceMeter

SourceMeter[95]提供了免费版本,可以为代码提供许多不同的度量,也可以调用cppcheck。

Bloaty McBloatface

Bloaty McBloatface[96]是用于类unix平台的二进制大小分析器。

参考资料

[1]C++ Best Practises: https://lefticus.gitbooks.io/cpp-best-practices/content/

[2]知识共享署名-非商业4.0国际许可协议: http://creativecommons.org/licenses/by-nc/4.0/

[3]GitHub: https://github.com/lefticus/cppbestpractices

[4]Learning C++ Best Practices: http://shop.oreilly.com/product/0636920049814.do

[5]GitHub: https://github.com/

[6]Bitbucket: https://bitbucket.org/

[7]SourceForge: http://sourceforge.net/

[8]GitLab: https://gitlab.com/

[9]Visual Studio Online: https://visualstudio.com/

[10]CMake: http://www.cmake.org/

[11]延伸阅读: https://lefticus.gitbooks.io/cpp-best-practices/content/10-Further_Reading.md

[12]Waf: https://waf.io/

[13]FASTBuild: http://www.fastbuild.org/

[14]Ninja: https://ninja-build.org/

[15]Bazel: http://bazel.io/

[16]Buck: http://buckbuild.com/

[17]gyp: https://chromium.googlesource.com/external/gyp/

[18]maiken: https://github.com/Dekken/maiken

[19]Qt Build Suite: http://doc.qt.io/qbs/

[20]meson: http://mesonbuild.com/index.html

[21]premake: https://premake.github.io/

[22]Conan: https://www.conan.io/

[23]hunter: https://github.com/ruslo/hunter

[24]qpm: https://www.qpm.io/

[25]build2: https://build2.org/

[26]Buckaroo: https://buckaroo.pm/

[27]Vcpkg: https://github.com/microsoft/vcpkg

[28]Travis CI: http://travis-ci.org/

[29]AppVeyor: http://www.appveyor.com/

[30]Hudson CI: http://hudson-ci.org/

[31]Jenkins CI: https://jenkins-ci.org/

[32]TeamCity: https://www.jetbrains.com/teamcity

[33]Decent CI: https://github.com/lefticus/decent_ci

[34]ChaiScript: http://chaiscript.com/ChaiScript-BuildResults/full_dashboard.html

[35]Visual Studio Online: https://visualstudio.com/

[36]GitLab: https://gitlab.com/

[37]Coverity Scan: https://scan.coverity.com/

[38]执行标准一致性: https://docs.microsoft.com/en-us/cpp/build/reference/permissive-standards-conformance

[39]Build EAR: https://github.com/rizsotto/Bear

[40]正常编译期间调用clang-tidy: https://cmake.org/cmake/help/latest/prop_tgt/LANG_CLANG_TIDY.html

[41]include-what-you-use: https://github.com/include-what-you-use

[42]示例结果: https://github.com/ChaiScript/ChaiScript/commit/c0bf6ee99dac14a19530179874f6c95255fde173

[43]clang-modernize: http://clang.llvm.org/extra/clang-modernize.html

[44]示例结果: https://github.com/ChaiScript/ChaiScript/commit/6eab8ddfe154a4ebbe956a5165b390ee700fae1b

[45]clang-check: http://clang.llvm.org/docs/ClangCheck.html

[46]clang-tidy: http://clang.llvm.org/extra/clang-tidy.html

[47]Coverity: https://scan.coverity.com/

[48]Travis CI: http://travis-ci.org/

[49]AppVeyor: http://www.appveyor.com/

[50]PVS-Studio: http://www.viva64.com/en/pvs-studio/

[51]Cppcheck: http://cppcheck.sourceforge.net/

[52]cppclean: https://github.com/myint/cppclean

[53]CppDepend: https://www.cppdepend.com/

[54]通过CMake使用: http://garykramlich.blogspot.com/2011/10/using-scan-build-from-clang-with-cmake.html

[55]基于llvm的工具: https://lefticus.gitbooks.io/cpp-best-practices/content/02-Use_the_Tools_Available.html#llvm-based-tools

[56]CodeChecker: https://github.com/Ericsson/CodeChecker

[57]Clang Power Tools: https://clangpowertools.com/

[58]命令行选项: http://msdn.microsoft.com/en-us/library/ms173498.aspx

[59]Flint: https://github.com/facebook/flint

[60]Flint++: https://github.com/L2Program/FlintPlusPlus

[61]OCLint: http://oclint.org/

[62]JetBrains: https://www.jetbrains.com/cpp/

[63]Cevelop: https://www.cevelop.com/

[64]clazy: https://github.com/KDE/clazy

[65]IKOS: https://ti.arc.nasa.gov/opensource/ikos/

[66]Github: https://github.com/NASA-SW-VnV/ikos

[67]Codecov: https://codecov.io/

[68]Coveralls: https://coveralls.io/

[69]LCOV: http://ltp.sourceforge.net/coverage/lcov.php

[70]Gcovr: http://gcovr.com/

[71]kcov: http://simonkagstrom.github.io/kcov/index.html

[72]OpenCppCoverage: https://github.com/OpenCppCoverage/OpenCppCoverage

[73]Valgrind: http://www.valgrind.org/

[74]american fuzzy lop: http://lcamtuf.coredump.cx/afl/

[75]LibFuzzer: http://llvm.org/docs/LibFuzzer.html

[76]KLEE: http://klee.github.io/

[77]Dextool Mutate: https://github.com/joakim-brannstrom/dextool/tree/master/plugin/mutate

[78]MuCPP: https://neptuno.uca.es/redmine/projects/mucpp-mutation-tool/wiki

[79]mull: https://github.com/mull-project/mull

[80]CCMutator: https://github.com/markus-kusano/CCMutator

[81]Krister的博客文章: https://kristerw.blogspot.se/2018/03/detecting-incorrect-c-stl-usage.html

[82]泄露到其他代码中: http://www.forwardscattering.org/post/48

[83]Google Test: https://github.com/google/googletest

[84]Catch: https://github.com/philsquared/Catch

[85]CppUTest: https://github.com/cpputest/cpputest

[86]Boost.Test: http://www.boost.org/doc/libs/release/libs/test/

[87]uftrace: https://github.com/namhyung/uftrace

[88]rr: http://rr-project.org/

[89]Lizard: http://www.lizard.ws/

[90]Metrix++: http://metrixplusplus.sourceforge.net/

[91]ABI Compliance Checker: http://ispras.linuxbase.org/index.php/ABI_compliance_checker

[92]Customizable Naming Convention Checker: https://github.com/mapbox/cncc

[93]ClangFormat: http://clang.llvm.org/docs/ClangFormat.html

[94]关于clang-format的系列文章: https://engineering.mongodb.com/post/succeeding-with-clangformat-part-1-pitfalls-and-planning/

[95]SourceMeter: https://www.sourcemeter.com/

[96]Bloaty McBloatface: https://github.com/google/bloaty

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8