Glory C++ 编码规范

概述

  • 见名知意:

    • 命名可以无限长。为了准确描述对象,无需简化用词(易造成歧义)
    • 如有缩写(acronym),必须补充完整含义
  • 无歧义

    • 当前代码段无歧义
    • 当前命名空间无歧义
    • 当前项目无歧义:需要对整个项目有较好的理解。
    • 当前系统无歧义:如尽量避免全局使用 ProcessInfo(Windows系统自带命名等) 等系统API对象
    • 广义上无歧义:需要更大的知识空间,可暂时略去
  • 注释

    • 注释是为了对命名的局限性进行补充;原则上,无法做到见名知意的任何对象,均应补充注释;
    • 注释应说明为什么而不是什么;
    • 注释规范,使用 doxygen 类似形式,示例如下:
    • 注释内容不要紧跟注释符 ‘//’ ,空一格
    /*
    @file      文档名,可以默认为空,DoxyGen会自己加
    @date      文档修改的日期:3/16/2006
    @author    作者名
    @version   版本标识
    @copyright 版权,GNU Public License.
    @remark    备注,或remarks
    @brief     简易说明,产生的文档不会缩进无关键词
    @details   详细说明,产生的文档不会缩进无关键词
    @pre       函数前置条件,比如对输入参数的要求
    @param     参数及其意义
    @return    返回值
    @see       引用
    @override  重写
    @throws    异常类及抛出条件
    @warning   函数使用中需要注意的地方
    @note      提醒
    @bug       bug
    @code      代码示例
    CDC* dc = AfxGetMainWindow()->GetDC();
      Bitmap bmp(dc , "test.bmp");
      AfxGetMainWindow()->ReleaseDC(dc);
    @endcode   代码示例结束
    */
    
    // 文件注释可以详细;
    // 类原则上必须注释;
    // 函数按需注释;
    // 变量注释如此处形式
    

细节及示例

注意:代码提交之前必须格式化,格式化规则见最后章节。

  • 文件名

    • 小写字母下划线分隔,如:user_model.huser_model.cc

    • 测试工程文件加后缀 ’xx_test.cc

      • 所有测试源码均放置于工程根目录的 /test 子目录下
      • 测试用例(函数)使用 xx_case() 形式,也可单独剥离为文件 xx_case.cc
    • 文件防重复包含均使用 #pragma once 而不是老旧标准

    • 文件 #include 路径应参考常规C++编码规范,如《C++ Primer》,概述为:

      #include "dir/foo.h"     // foo.cc 优先包含自身头文件
      #include <cmath>         // c
      #include <fstream>       // c++
      #include <eigen_util.h>  // base repository
      #include "user_model.h"  // current project
      
  • 类名、类型定义、命名空间等

    • 大驼峰
    • 不要在头文件的全局作用域引入某个命名空间
    • 如无必要,不使用多重继承
    • 如无必要,不使用运算符重载
    • 如无必要,不使用友元
    // namespace 例外(参考STL)
    namespace ipec
    {
    
    // 命名空间内的内容无需缩进。如不匹配,格式化规则(最后章节)会自动对齐该规则。下同,均略
    template <typename T>
    class Point
    {
    }
    
    }  // namespace ipec
    
    template <typename T>
    struct PointHash
    {
        std::size_t operator()(const ipec::Point<T>& point) const
        {
            std::size_t h1 = std::hash<T>{}(point.x());
            std::size_t h2 = std::hash<T>{}(point.y());
            return h1 ^ (h2 << 1);  // Combine the hashes
        }
    };
    
    using IntPoint    = ipec::Point<int>;
    using DoublePoint = ipec::Point<double>;
    
  • 成员变量(重要!)

    必须以下划线结尾,以便与局部变量区分。

    namespace Chemical  // 建议改为小写
    {
    
    class Compound
    {
    public:
    	explicit Compound(std::vector<Element> const &elements);
    	explicit Compound(
    		std::string const &chemical_formula,
    		std::string const &name				       = "",
    		double			       excitation_energy = 0);
    	bool Invalid() const
    	{
    		return elements_.empty();
    	}
    	std::string String() const;
    
    public:	 // simple class simple use, no need to getElements
    	std::vector<Element> elements_;
    	std::string			     name_;
    	std::string			     formula_;
    	double				       excitation_energy_;
    };
    
    }  // namespace Chemical
    
  • 函数

    • 大驼峰——整体雷同 Google编码风格。
    • 如无必要,不使用异常
    // interface 以 I 开头
    class UserController
    {
    public:
    	enum UrlTableErrors {  // 嵌套类型的定义放在前面而不是后面
    		kOK = 0,
    		kErrorOutOfMemory,
    		kErrorMalformedInput,
    	};
    public:  // 不要吝啬 public:类型与成员函数要分开
    	bool HasPermission() const;  // Google 的大驼峰形式(客户✅); 个人更偏向: has_permission
    protected:
    	bool doSth();  // 非 public 成员函数以小写字母开头(客户✅); 本人更偏向: do_sth_ 或 _do_sth
    private:
    	Channel* channel_; // 头文件中只使用了指针或引用则使用向前声明,而非头文件
    }
    
  • 其他

    整体符合 Google编码风格。请自行使用 C++ 创始人 BS 的《 The C++ Programming Language 》汲取细节,此处仅挑出易被忽视的重点部分或模糊部分:

    • 缩进:4 个空格(方便列编辑以及更好的web端展示)
    • 如无必要,不使用宏定义;如使用,应全部大写
    • 枚举类型使用最新的 c++ 规范而不是 C 规范:enum class
    • Windows、Qt、CUDA 均有自身的编码规范。可取交集,以子领域优先为原则
    // 复杂类型必须使用 using 声明,而不是 typedef
    template <typename T> using twin = pair<T,T>;
    
    // 常量、枚举值均以 k 开头
    const int kDaysInAWeek = 7;
    
    // 局部(临时)变量也使用下划线风格
    int num_users;
    
    // 重载时必须 override
    virtual void DoSth() override;
    
    // 单参构造函数必须使用 explicit 修饰
    explicit UserController(int xx);
    
    // #if 等宏定义最好顶行书写
    #if __cplusplus >= 202002L
        // supports C++20
    #else
        // non-support C++20
    #endif
    
    // 尽量加括号,不要让读者产生额外负担(虽然不必要)。
    // 如:
    num_users >> 1+2;
    // 应尽量写为:
    num_users >> (1+2);
    
    • 优先级 概览:
      优先级(高→低)运算符说明结合性
      1::作用域解析(scope resolution)自左向右
      2() [] . ->函数调用、下标、成员访问、指针成员自左向右
      3++ -- (后缀)后自增、后自减自左向右
      4++ -- (前缀)前自增、前自减自右向左
      ! ~逻辑非、按位取反自右向左
      + - (一元)正号、负号(单目)自右向左
      * &取值(解引用)、取地址自右向左
      () (强制类型转换)C 风格/函数风格强制转换自右向左
      sizeof取大小自右向左
      new new[] delete delete[]动态内存分配与释放自右向左
      5* / %乘、除、取余自左向右
      6+ -加、减自左向右
      7<< >>位移运算自左向右
      8< > <= >=关系运算符(大小比较)自左向右
      9== !=相等、不等自左向右
      10&按位与自左向右
      11^按位异或自左向右
      12``按位或
      13&&逻辑与自左向右
      14``
      15?:条件运算符(三目)自右向左
      16= += -= *= /= %= <<= >>= &= ^= `=`赋值及复合赋值运算符
      17throw抛出异常自右向左
      18,逗号运算符自左向右

格式化

  • 代码提交前必须使用clang-format格式化
  • 已置于各项目根目录,如缺失可自行添加
  • 可根据需要定制、修改
BasedOnStyle: Google
# UseTab: Always
TabWidth: 4
IndentWidth: 4
ColumnLimit: 80
Cpp11BracedListStyle: false
SeparateDefinitionBlocks: Always
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignTrailingComments: true
AlignArrayOfStructures: Left
AccessModifierOffset: -4
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
BreakConstructorInitializers: AfterColon
PackConstructorInitializers: NextLine
BinPackArguments: false
BinPackParameters: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: false
IndentExternBlock: false
IndentWrappedFunctionNames: false
MaxEmptyLinesToKeep: 1
SortIncludes: false
# InsertNewlineAtEOF: true
InsertTrailingCommas: Wrapped
EmptyLineBeforeAccessModifier: LogicalBlock
FixNamespaceComments: true
CompactNamespaces: false
BreakStringLiterals: true
AllowAllArgumentsOnNextLine: false
AlwaysBreakAfterReturnType: None

AllowAllParametersOfDeclarationOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true

BreakBeforeBraces: Custom
BraceWrapping:
  AfterCaseLabel: true
  AfterClass: true
  AfterEnum: true
  AfterFunction: true
  AfterNamespace: true
  AfterStruct: true
  AfterUnion: true
  IndentBraces: false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
  BeforeLambdaBody: true