Avplayer.org coding standard [草案]


#1

经过 avplayer 半年多的社区实践, 我觉得是时候整理出本社区的C++项目所要遵循的编码标准了. 现在还只是个草案, 希望大家踊跃提出自己的建议


  • 目录结构

avplayer 社区 C++ 项目分为 library 和 standalone executable 两类.

    • 库项目 目录结构

avplayer 编码标准要求所有的 library 符合如下的目录结构

   
avplayer/
|-- CMakeLists.txt
|-- doc/
|   `-- html/
|-- examples/
|   |-- example1.cpp
|   `-- example2.cpp
|-- include/
|   |-- avplayer/
|   |   |-- detail/
|   |   |   `-- detailheader.hpp
|   |   |-- header1.hpp
|   |   |-- header2.hpp
|   |   `-- impl/
|   |       `-- impl.ipp
|   `-- avplayer.hpp
`-- src/
    |-- libsrc1.cpp
    `-- libsrc2.cpp

如果是 header only 的库, 不需要有 src 目录.

头文件必须放到 include/项目名 目录下. 这样的好处是避免文件名冲突. 为了方便, 还可以提供 include/项目名.hpp 这样一个总的头文件, 将其他文件全部包含.

include/项目名/ 目录下直接存放的文件是用户可以直接 include 的头文件. 例如用户不使用全部功能的时候可以只 include 其中的一个文件来使用其中一个功能. include/项目名/detail 下存放是是实现定义的其他头文件. 不允许用户直接包含.

如果项目打算允许 用户选择"header only" 还是单独编译的, 通常会选择将实现写入 ipp 文件, 并通过条件编译选择是否在主头文件里包含. 那么可以将 ipp 文件放到 include/项目名/impl 目录下.

这样以来, 一个 header only 的库, 用户使用 git submodule 即可将代码如数囊括, 并简单的添加编译器路径查找 为 “项目目录/include” 即可使用.

可以参考 avhttp 的目录结构. 以及 avproxy avhttpd deCAPTCHA .

    • 可执行文件项目

可执行文件项目没有强制性的要求. 这通常是依据作者的喜好. 将所有的 header only 的库使用 git submodule 包含进来是最佳工程实践, 也是唯一强制要求.

  • 文件名规范

C++ 源码统一使用 cpp 扩展名, 头文件统一使用 hpp 扩展名, 但是一些放到头文件实现的代码, 准许使用 ipp 扩展名. 并放到相应的目录下.

文件名统一使用小写字符, 不允许使用大写字符.

需要分词的, 准许使用 下划线. 例如 error_code.hpp

不允许出现双下划线.

  • 代码排版

排版缩进推荐使用 Tab , 当然空格也可以 . 当我说缩进一个单位, 意思是一个 Tab , 或者利用空格缩进, 则是 2 4 8 个空格任选. 当然, 同一个项目要将一个单位缩进的空格数固定, 不允许混用 Tab 和空格缩进

    • { 位置.

除了在 namespace foo {do {这样的位置, { 放到行尾, 其余 { 全部单独一行

while () {} 语句里, { 也独立一行.

    • } 位置

} 一律单独成 一行. 除了在 do {} while 里, } 和 while 在一行.

    • if while for 后面的 (), 前后留空. 如 if (XXX) , while (XXX) . 括号内不留空. 如果 ) 在行尾则不留空格, 避免行尾出现空白
    • 逗号表达式(函数调用, 参数列表), 每个逗号后面留一个空格.
    • for 里每个 ; 后面留一个空白. 除非是 for (;:wink: 这种死循环, 里面不要留任何空白.
    • 缩进.

namespace 不缩进. 但结束 namespace 的那个 } 后必须带 // namespace foo 这样的注释.

class 里, public/priviat/protected 关键字不缩进. 成员定义缩进一个单位

{ } 包围的代码块, 内部语句缩进一个单位.

{ 和 switch 平其, 其 case 语句缩进一个单位. case 下语句不管有没有使用 {} 包围, 缩进一个单位.

    • 换行

代码宽度限制为 80 列或者 120 列. 禁止超过 120 列的代码. 80 列为软限制. 允许 换行破坏美观 的代码稍微超过 80 列.

    • 文件尾空行

规定每个文件尾必须空行。

      • if/while 条件表达式换行

        if/while 语句内表达式过长, 超过 80列边界, 需要换行. 如果是因为多个表达式, 每个表达式一行.

        && || 连接符出现在下一行. 如下所示

if/while (XXXXXXXXXXXX expression
   || YYYYYYYYYYY expression)
{

}

虽然不建议, 但是如果 条件表达式真的过于复杂而不能有其他办法化简, 则建议下面的风格

      > 
if/while (
   XXXXXXXXXXXX expression
   || YYYYYYYYYYY expression
)
{

}
      > 让  ( ) 各自独立一行, 然后表达式多行, 并缩进. 看起来就非常清晰了.
      • 函数调用换行

        调用 函数 难免出现过长的问题. 有两种换行办法 , 一种是直接 80 个字符处换行. 但是要在逗号处换行. 第二行开始参数缩进一个单位. ); 附在最后一行行尾.

        > 另一种是每个参数独立一行.  每个参数独立一行, 则 ); 独立一行. ( 不独立一行, 跟随函数末尾.  参数缩进一个
        
        > 单位, 如
        
 call_foo(xxx, xxxxxx, xxxxxxxx, 
       yyy, yyyyyyyyy);

 call_bar(
       xxxxx,
       xxxxx,
       yyy,
       boost::bind(xxx, xxx, xxx)
 );
     > 第二种办法  特别适合参数特长, 尤其是参数带有函数嵌套的, 如有一个参数是 boost::bind 的返回值. 应该说,  参数里还有嵌套的调用的, 必须使用第二种办法缩进排版.  *注意*,第一个缩进办法只能使用两行,超过两行不允许使用第一种缩进格式
      • 函数参数换行

      函数参数换行, 要求第二行开始缩进一个单位即可.

      • class 初始化参数换行

        class 初始化参数, 另起一行. 相对构造函数缩进一行, 第一行的 : 后空一空格写第一个成员

        接下来, 每一个成员的一行, 逗号(,) 放行首, 与 : 平齐. 后空一空格. 如

 void abc::abc(xxx)
       : m_abc(0)
       , m_cba(0)
 {
       // code
 }

如果初始化参数里有较长的调用的, 意味着 () 内部要换行, 其换行模式参考 函数调用.

    • 指针类型的位置

    char* ptr; 而不是 char ptr; 将 * 和 char 放到一起. 因为 char 整体是一个类型.

有人说 char* ptr1, ptr2; 导致 ptr2 不是 char* 类型, 所以应该放靠近变量名.

我的回答是, 这个是 C 的缺陷, 请不要使用这种方式连续定义变量.

  • 注释

    对于 library 项目来说, 所有公共接口必须有详细的注释! 注释必须解释清楚接口的详细用法. 可以在注释中给出调用例子. 必须详细解释每个参数的用途. 参考 avhttp 的头文件注释.

最后给一下效果

// Indentation
#define foobar(A)\
     {Foo();Bar();}
#define anotherFoo(B)\
     return Bar()

namespace Bar {

class Foo
{
public:
	Foo();
	virtual ~Foo();
};

switch (foo)
{
	case 1:
		a += 1;
		break;

	case 2:
	{
		a += 2;
		break;
	}
}

if (isFoo)
{
	bar();
}
else
{
	anotherBar();
}

int foo()

while (isFoo)
{
	...
	goto error;
	....
error:
	...
}

} // namespace  Bar

foo Array[] = {
    red,
    green,
    darkblue
};

foo Function(barArg1,
    barArg2,
    barArg3);

struct foo
{
	int bar() {}
};

// Formatting
void func()
{
	if (isFoo(a, b))
		bar(a, b);

	if (isFoo)
		a = bar((b - c) * a, *d--);

	if (isFoo(a, b))
		bar(a, b);

	if (isFoo)
	{
		isFoo = false;
		cat << isFoo << endl;
	}

	if (isFoo)DoBar(); if (isFoo)

	{
		bar();
	}
	else if (isBar())
	{
		annotherBar();
	}

	int var = 1;
	int* ptr = &var;
	int& ref = i;

	QList<int>::const_iterator it = list.begin();
}

namespace A {
namespace B {

class someClass
{
	void foo()
	{
		if (true)
		{
			func();
		}
		else
		{
			// bla
		}
	}
};

} // namespace A
} // namespace B


#2

@Jackarain @hyq @omegacoleman @q492607291 @dpainter @avdbg @abinghu @cozyboy @zxf 都来评价啊 …

100 多人, 咋 @ 啊


#4


#5

s/standart/standard/


#6

你是对的!不明觉厉


#7

俺还没写过C++代码,纯粹是来学习的。

另外,虽然我没使用过C++的静态代码检查工具,但是从Java项目中使用静态代码检查工具的效果来看,还是很有必要和好处的。

因此,建议在适当的时候考虑加入一些静态代码检查工具的要求和约束。google了一把,支持C++的有PC-Lint、Cppcheck等。可考虑作为代码提交前的自查内容之一,也可以选择其检查的部分规则,作为抽查方式。


#8

要求这些真的一点意义没有,有好习惯的无论是什么习惯都好,没有的直接astyle


#9

有代码规范和必要的说明,能否再加入类结构图,这样在维护时,能够知道彼此的关联!


#10

一个人写没有问题,你自己的习惯就好,但是如果项目大有很多人参与的话,就必须要指定一些规范了。


#11

有规范挺好的。

这两个看着怪怪的,*和&号两边都有空格吗?


#12

统一用astyle格式化不OK了~


#13

要知道说的容易做的难啊!!!


#14

不管本身编码习惯好坏。

提交代码之前知道用工具格式化一下代码也是好的。这年头,你先明白了不允许的事情也不少人去做,更别说没有明确说不能做的事情了。


#15

不明觉历,条款太多了,我会慢慢学习的


#16

空格和tab真得是一对冤家呀,很多编辑器都有自动把tab替换成空格的功能。

我建议如果缩进用空格,至少4个吧,2个我觉得少了点,尤其是现在屏幕都大了,4个区分得更明显一点。


#17

听好的 就是 { }单独占一行,太占空间了。 建议if/while的{ 就跟在后面,省出一行的空间。


#18

那不行! 要有统一性!


#19

本主题已全局置顶,它将始终显示在它所属分类的顶部。可以由版主解除置顶或者点击清除置顶按钮。


#20