OpenVDB 的命名规范
介绍:
本文档详细地规定了编码的实践,这应用于OpenVDB的代码库。提交的代码应该遵循这些规定,来维护一致性和可维护性。 在C++编程语言的软件工程之中,团队协作开发中,这个规范是
有借鉴意义的。
命名空间
1 小写,保持简单和短小(例如 tools ,tree)
2 使用 internal (或者 detail) 或者 module_internal 作为同义词,例如模板
必须被在头文件中定义,但是它的实现细节是不要用于公开用途的。
类与结构体
1 大小写混合,首字母大写(例如 AffineMap,TreeIterator)
2 不使用前缀
类方法
1 大小写混合,首字母小写(例如 getAccessor(),gridType())
2 读取方法是返回引用的成员变量或者是值的原生的类型的,它的名称仅是变量名称(例如Grid::tree())
3 读取方法包括了对象的组装或者是其它的计算的,它的名称是get加上变量名称(例如Grid::getAccessor())
4 简单的操作方法的名称是set加上变量名称
类实例的变量
1 大小写混合,总是以m为前缀(例如mTree,mTransform)
2 在公开的API中使用的struct的成员的名称的m前缀可以省略(例如struct Color{float r,g,b;})
类的静态变量
1 大小写混合总是以s为前缀(例如sInitialized)
局部变量和参数
1 大小写混合以小写字母开头(例如ijk,offset,range,rhsValue)
常数
1 所有的字母都是大写,单词间以下划线分隔。如果不在一个命名空间,或者
一个源文件的局部,以库名称为前缀 (例如HALF_FLOAT_TYPENAME_SUFFIX,
ZIP_COMPRESSION_LEVEL)
枚举名称
1 混合大小写,首字母大写(例如 GridClass,VecType)
枚举数值
1 在枚举中,所有的单词都大写,以下划线分隔和一个共同的前缀(例如GRID_LEVEL_SET,
VEC_INVARIANT)
2 在枚举类中,大小写混合,首字母大写(例如 GridClass::Unknown,GridClass::LevelSet)
类型定义
1 大小写混合,首字母大写(例如 ConstPtr,ValueType)
2 不使用前缀
全局变量
1 大小写混合,总是以g为前缀(例如gRegistry)
2 总之,尽量以类的静态数据代替全局变量
全局函数
1 大小写混合,总是以g为前缀(例如gFunc())
2 总之,尽量以类的静态成员代替全局函数
布尔类型
当命名布尔函数和变量时,使用作为条件的名称(例如 if(grid.empty())
if(matrix,isInvertible()))
实践
通用规则
1 在默认的警告级别下,代码必须编译而没有任何警告消息。
2 C++的标准库优先于C标准库
3 限制变量为最小的可能的作用域,避免赋值前的定义局部变量。条件内的声明优先于
先赋值,再在条件中使用。
4 对于新的文件,确保使用正确的许可。
格式化
1 缩进是4个空格符,不使用TAB
2 每行不超过100个字符。
3 使用Unix风格的回车符"\n",而不是windows/Dos的“\r\n”
4 不要留下调试用的printf或者是其它的调试代码
5 在一组变量声明与其它的代码之间留下一个空行。
6 关键字if,switch,while,do,for,和return 之后,留下一个空格符。
7 在二元操作符例如+,-,*,/,&&,||的两边各留下一个空格符。在数学的情况下,为了清晰,你可以为了强调
*,/相对于+,-的优先级,而省略了*和/的两边的空格符。
8 在任何的引用的操作符(例如*,&,[],->,.)与操作数之间不要留下空格符。
9 在参数列表中,每个逗号之后,保留一个空格符。
10 左括号的后面,右括号的前面不要留下空格符
11 在表达式中要明确操作符的优先级,应该使用括号。
12 在结束语句的分号之前,不要留下空格符。
13 在字符串或者是字符常量中,不要使用TAB,而是使用空格符或者是“\t”
14 如果一个参数太长了,在参数之间,截掉它。如果合适的话,在行上分组相关的参数
15 为了竖向对齐相关的表达式,修改空格是允许的。
16 数值常量总是以数字开头(例如 是0.001而不是 .001)
17 在公开的代码中,使用K&R风格的括号对齐
18 在单行的流控制语句中,你可以为了简单而不写括号。
19 在函数的定义中的返回类型应该独占一行。
包括声明
1 包含的头文件与你的源代码在相同的目录下时,你总是使用双引号
2 包含的头文件与你的源代码不在目录的内部时,你总是使用尖括号。
3 在包含指定时,不要使用绝对路径
4 对于一个给定的源文件,如果有一个头文件,首先列出它,接着是
其它的局部头文件,代码库头文件,和系统头文件。
头文件
1 头文件的扩展名称是.h
2 所有的头文件应该被括在 "#ifdef"保护语句中。
3 在类定义中,先列出公共的,然后是保护的,再下来是私有的成员。
4 在变量之前列出方法
5 完全原型化所有的公共的函数,对于每个参数使用描述性命名
6 在头文件中,声明每个函数定义,除了类定义之外的显式的声明为inline
7 前向声明优先于头文件中 #include 声明
8 不要利用头文件中的间接包含。
9 不要在头文件中使用匿名的命名空间。
源文件
1 源文件有扩展名称为.cc
2 使用有用的命名参数,适当地原型化所有的文件静态函数
3 只要可能的话,把变量和函数放入一个匿名的命名空间
4 避免全局变量
注释
1 使用//风格的注释代替/**/风格,甚至对于多行注释也是如此。
2 使用多行注释描述如下的代码段
3 使用行尾注释描述变量声明或者是明确单语句的含义。
4 大块的代码块应该用 #if 0 和 #endif进行注释掉。
5 作为一个历史的负债,不要用注释留下废弃的代码。
6 用Doxygen注释的文档化公共代码。
原生的类型
1 避免写的代码依赖于原生的数值的比特位大小,除非当特殊的比特位大小被要求时,
使用显式的类型例如,int32_t,uint_64.
宏
1 避免常数的宏。使用全局静态的常数来代替。(局部静态常数
在一个线程安全的方式中不保证被初始化)
2 避免函数的宏。使用inline和模板来代替。
类
1 调用仅有一个参数的构造方法,应该用显式的关键字为前缀,来避免发生非期望的类型转换。
2 从构析方法中,不要调用虚拟方法
3 如果你有一个复制的构造方法,确保你有一个赋值的操作符。
4 如果你有一个赋值的操作符,你可能需要一个复制的构造方法。
5 如果你的数据成员指向动态分配的内存,确保你有一个复制的构造方法和一个赋值的操作符,
它们都对它们的内存做正确的事。
6 能成为子类的类总是有一个虚拟的构析方法,即使它是空的。
7 检查自赋值,在赋值操作符中返回*this.
8 只要可能 就声明方法为const
9 只要可能 声明对象的值的输入参数为const引用,原生的可能是传值的。
10 算术的,逻辑的,比特位的,引用的,操作符的地址 应该仅使用于它们的语义是明确的,明显的,无歧义的。
11 对于函数,应用的操作符()是被允许的。
12 转换操作符应该被避免使用。
13 不要返回栈分配的引用或者是隐式的计算对象。
14 如果一个类没有复制的构造方法和赋值操作符,考虑创建一个私有的非实现的复制构造方法
和赋值操作符,来阻止自动生成的版本。
条件声明
1 对于测试表达式,使用一个形式上尽可能清晰的操作数的类型,避免隐式的转换。
2 在作用域上,条件语言上发生的赋值必须没有影响。
3 当比较浮点数的值时,允许算术上的不精确。
4 在switch语句中,总是注释失败情况和空的情况
命名空间
1 如果可能,应该使用命名空间
2 避免使用using直接拉整个命名空间(例如 using namespace std)
3 对于单独的符号,允许使用using声明(例如 using std::vector)但它们不应该
出现在一个头文件中
4 在它们的参数的命名空间中,定义全局的操作符
5 命名空间不可以缩进
异常
1 适度使用异常是被鼓励的。
2 方法应该使用注释来声明它们可能抛出的所有的异常,但是不是异常的规范
3 抛出的范围是局部的异常实例,而不是指针,引用,或者是全局的。
4 通过引用捕捉异常
5 不允许一个异常从一个构析方法中逃脱。
模板
1 当声明模板类型参数时,使用 typename而不是class。
其它
1 遍历非原生的类型的数组,不要使用指针。使用显式的数组索引,迭代器或者是通用的算术。
2 使用C++的转换(static_cast<int>(x)) 而不是C的转换((int)x)
3 如果变量相关的话,相同类型的多个数据类型的变量可能 被声明为同一行中。
4 在一个错误的条件的响应中,库代码必须 不能直接中止。
5 当能使用new/delete来代替时,避免使用malloc,free.
6 避免使用goto
7 避免魔法数字。如果必要的话,使用命名的常数。
8 如果你使用 typeid/typeinfo,要注意,尽管所有的运行时,都支持typeinfo::name()
它返回的字符串的格式,在不同的编译器,甚至是同一个编译器时,不能保证是唯一的。