最近看了一下 DPDK Coding Style,做了一下总结,分享一下。英文较好的可以直接看下面的链接。
DPDK Coding Stylehttps://doc.dpdk.org/guides/contributing/coding_style.html
那么直接开始。
1 General Guidelines - 通用准则
1. 建议行长不要超过80个字符,包括注释。
2. Tab应该假定为8个字符宽。
2 C Comment Style - C的注释风格
2.1 通用注释
非常重要的单行注释像这样
/*
* VERY important single-line comments look like this.
*/
大多数单行注释像这样
/* Most single-line comments look like this. */
多行注释像这样。让他们成为真正的句子。
/*
* Multi-line comments look like this. Make them real sentences. Fill
* them so they look like real paragraphs.
*/
2.2 License Header - 许可证头
每个文件必须以包含软件包数据交换 (SPDX) 许可证标识符的特殊注释开头。
在任何版权标题之后,在任何其他内容之前应留一个空行,例如在 C 文件中的include语句。
3 C Preprocessor Directives - C预处理器指令
3.1 Header Includes
include文件应该按照以下顺序排序:
- libc includes (system includes first)
- DPDK EAL includes
- DPDK misc libraries includes
- application-specific includes
来自本地应用程序目录包含的使用引号""包含,而来自其他路径的包含文件使用尖括号<>包含。
按照四个顺序排序后的例子如下:
#include <stdio.h>
#include <stdlib.h>
#include <rte_eal.h>
#include <rte_ring.h>
#include <rte_mempool.h>
#include "application.h"
注:这个后续如果要模仿可以直接参考其他的pmd。
3.2 Header File Guards - 头文件保护
头文件应该被保护免于通常的多重包含:
#ifndef _FILE_H_
#define _FILE_H_
/* Code */
#endif /* _FILE_H_ */
3.3 宏
不要#define或者声明名字除非使用标准的DPDK前缀:RTE_。
“不安全”宏(有副作用的宏)的名称以及清单常量的宏名称都是大写的。
类似表达式的宏的扩展要么是单个标记,要么具有外括号。
如果宏是函数的内联扩展,则函数名称全部小写,宏具有相同的名称,全部大写。
如果宏封装了复合语句,请将其包含在 do-while 循环中,以便可以在 if 语句中安全地使用它。 任何最终语句终止分号都应由宏调用而不是宏提供,以使漂亮的打印机和编辑器更容易进行解析。
NOTE:
枚举和内联函数应该优先于宏。
3.4 条件编译
NOTE:
仅在绝对必要的情况下使用条件编译。
当使用 #ifdef 或 #if 条件编译代码时,且中间有一大片代码或者有多个 #ifdef 嵌套,可以在匹配的 #endif 或 #else 之后添加注释。
注释应与#endif 或#else 用一个空格分隔。
4 C Types
4.1 Integers - 整型
该项目使用uintXX_t形式。
4.2 枚举
枚举值全部大写。
枚举类型应该以“rte_”为前缀,元素应以合适的前缀开头以避免命名空间冲突[通常以 RTE_<enum>_ 开头 - 其中 <enum> 是枚举类型的简称]。
4.3 变量声明
在声明中,不要在星号和相邻标记之间放置任何空白,与类型相关的标识符的标记除外。
例如:
int *x; /* no space after asterisk */
int * const x; /* space after asterisk when using a type qualifier */
所有外部可见变量的名称中都应包含 rte_ 前缀,以避免命名空间冲突。
不要在变量名称中使用大写字母 - 无论是 ALL_UPPERCASE 形式还是 CamelCase 形式。 仅小写字母和下划线。
4.4 结构声明
一般来说,在新结构中声明变量时,先按用途排序,然后按大小(从大到小)排序,最后按字母顺序排序。 按用途排序意味着常用变量一起使用,并且结构布局具有逻辑意义。 按大小排序可确保向结构中添加尽可能少的填充。
对于现有结构,出于向后兼容性的原因,应将结构的添加添加到末尾。
每个结构元素都有自己的行。
尝试通过使用空格对齐成员名称来使结构可读,如下所示。
极长类型后面的名称因此不易与其余类型对齐,应使用单个空格分隔。
struct foo {
struct foo *next; /* List of active foo. */
struct mumble amumble; /* Comment for mumble. */
int bar; /* Try to align the comments. */
struct verylongtypename *baz; /* Won't fit with other members */
};
主要结构应在使用它们的文件的顶部声明,或者如果在多个源文件中使用它们,则应在单独的头文件中声明。
结构的使用应通过单独的变量声明进行,并且如果在头文件中声明这些声明,则这些声明必须是 extern 的。
外部可见的结构定义应具有以 rte_ 为前缀的结构名称,以避免命名空间冲突。
5 C Indentation - C缩进
5.1 通用
用Tab缩进而不是空格。
Note:
DPDK中的全局空白规则,使用制表符缩进,使用空格对齐。
请勿在制表符前添加任何空格以进行缩进。
如果必须换行很长的语句,请将运算符放在行尾,然后再次缩进。
对于控制语句(if、while 等),建议将下一行缩进两个制表符,而不是一个制表符。
不要在行尾添加空白。
不要在文件末尾添加空白或空行。
5.2 Control Statements and Loops - 控制语句和循环
在关键字(if、while、for、return、switch)后添加一个空格。
不要对包含零个或只有一条语句的控制语句使用大括号 {}。
for循环的某些部分可以空着。
右大括号和左大括号与 else 关键字位于同一行。
5.3 Function Calls - 函数调用
函数名称后请勿使用空格。
逗号后面应该有一个空格。
( 或 [之前 、 ] 或 ) 之后没有空格。
5.4 Operators - 运算符
一元运算符不需要空格,二元运算符则需要。
不要使用括号,除非需要它们来保证优先级,或者除非没有它们会使语句混乱。
5.5 Exit
成功时退出应为 0,失败时退出应为 1。
5.6 Local Variables - 局部变量
变量应该在代码块的开头而不是中间声明。 例外情况是当变量为 const 时,在这种情况下声明必须位于首次使用/赋值时。
在函数中声明变量时,每行多个变量是可以的。
请注意不要通过在声明中初始化变量来混淆代码,只应初始化一行中的最后一个变量。
不要在初始化程序中使用函数调用,const 变量除外。
5.7 Casts and sizeof - 强制转化和sizeof
强制转换和 sizeof 语句后面不跟空格。
始终使用括号编写 sizeof 语句。 冗余括号规则不适用于 sizeof(var) 实例。
6 C Function Definition, Declaration and Use - 函数定义、声明和使用
6.1 原型
建议(编译器通常要求)所有非静态函数都在某处原型化。
一个源模块的本地函数应声明为静态,除非绝对必要,否则不应原型化。
代码其他部分(外部 API)使用的函数必须在相关包含文件中进行原型设计。
函数原型应按逻辑顺序列出,最好按字母顺序列出,除非有令人信服的理由使用不同的顺序。
在多个模块中本地使用的函数会放入一个单独的头文件中,例如“extern.h”。
作为外部 API 一部分的函数必须在函数名称上包含 rte_ 前缀。
不要在函数名称中使用大写字母, 仅小写字母和下划线。
短函数原型应包含在一行中。 更长的原型,例如 那些有很多参数的,可以分成多行。
Note:
与函数定义不同,函数原型不需要将函数返回类型放在单独的行上。
6.2 定义
函数类型应该单独位于函数之前的一行中。
函数体的左大括号应该单独成一行。
如下:
static char *
function(int a1, int a2, float fl, int a4)
{
不要在其他函数内声明函数。
长参数列表应该按照上面函数原型部分中的描述进行包装。
所有的主要函数都要有一个注释来描述它的用途。
7 C Statement Style and Conventions - C 语句风格和约定
7.1 空指针
NULL是首选的空指针常量。
测试指针是否为NULL时,如下:
if (p == NULL) /* Good, compare pointer to NULL */
if (!p) /* Bad, using ! on pointer */
7.2 返回值
创建对象或分配内存的函数应返回指针类型,并在出错时返回 NULL。 应通过适当设置变量 rte_errno 来指示错误类型。
处理数据包突发的函数(例如类似 RX 或类似 TX 的函数)应返回处理的数据包数量。
其他返回 int 的函数通常表现得像系统调用:成功时返回 0,出错时返回 -1,设置 rte_errno 来指示错误的具体类型。
如果给定库中已经标配,则可以使用替代错误方法,其中负值不是 -1,而是 -errno(如果相关),例如 -EINVAL。 但请注意,为了允许返回整数或指针类型的函数之间保持一致,任何新库都首选以前的方法。
对于不可能出错的函数,函数类型应该是 void 而不是 int。
返回 void * 的例程不应将其返回值转换为任何指针类型。
return 语句中的值不应包含在括号中。
7.3 Logging and Errors
使用DPDK提供的日志接口,不要用printf。
7.4 分支预测
当在关键区域(经常调用或在data path中调用)进行测试时,代码可以使用likely()和unlikely()宏来指示预期的或首选的快速路径。 它们被扩展为内置编译器,并允许开发人员指示是否可能采用该分支。
Note:
只能在性能关键路径中使用likely()和unlikely(),并且只有当存在明确的首选路径或通过这样做获得可测量的性能提升时才应使用。 在非性能关键代码中应避免使用这些宏。
7.5 静态变量和函数 Static
文件本地的所有函数和变量都必须声明为 static,因为它通常可以帮助编译器进行一些优化(例如内联代码)。
应内联的函数应声明 static inline,并且可以在 .c 或 .h 文件中定义。
Note:
头文件中定义的静态函数必须声明为 static inline,以防止编译器发出有关该函数未使用的警告。
7.6 Const 属性
当变量是只读的时,应尽可能频繁地使用 const 属性。
7.7 控制语句
永远循环是通过 for 语句而不是 while 语句完成的。
switch 语句中级联的元素应该有 FALLTHROUGH 注释。 例如:
switch (ch) { /* 缩进 switch. */
case 'a': /* 不要缩进 case */
aflag = 1; /* 缩进case主体一个 tab. */
/* FALLTHROUGH */
case 'b':
bflag = 1;
break;
case '?':
default:
usage();
/* NOTREACHED */
}
以上就是DPDK coding style的大致内容总结,对我这种野路子来说,一个相对规范的coding style学习还是很有必要的,起码代码写出来看起来不会那么丑,哈哈哈。
如果觉得这篇文章有用的话,可以点赞、评论或者收藏,万分感谢,goodbye~