Code Style - changnet/MServer GitHub Wiki

代码风格

文件编码

UTF-8 no bom,在任何平台下编辑,都使用\n换行。

文件名

小写加划线。如果文件定义的是类或者模块,则文件名必须和类名(或模块名一致)。C++的头文件使用hpp作为后缀,cpp文件则使用cpp后缀。 如果一个头文件即会在C中使用,又会在C++中使用,则使用h后缀。部分编译器会警告使用h后缀作为C++的头文件。

code_style.hpp
code_style.cpp
code_style.lua

则对应的类为

// C++ class
class CodeStyle
{
}
-- lua class
local CodeStyle = oo.class(...)

缩进

4个空格

类名、模块名

大驼峰,这样更容易和普通变量、函数区分开来

// cpp class
class CodeStyle
{
};

// 小写的是变量,与数据类型区分
CodeStyle code_style;
-- lua class
local CodeStyle = oo.class(...)

-- lua模块名也使用大驼峰
Json = {}

-- lua原生的模块(如string、table)则为小写,不需要理会

函数名

小写加下划线

仅部分特殊的函数允许不一样,例如:

// C++ class
class CodeStyle
{
public:
    // 构造函数和析构函数特殊处理
    CodeStyle();
    ~CodeStyle();

    // 普通函数以小写加下划线命名
    void set_mask(int mask);
};
-- lua class
local CodeStyle = oo.class(...)

-- lua中的构造函数,名字特殊处理,来自lua初始的模块特殊文件__init.lua
function CodeStyle:__init()
end

下划线开头的变量表示预留或者特别需要注意的变量

#ifdef _MSC_VER // 编译器预留的变量

__libc_malloc(); // glibc内置函数,使用时需要特别注意

这类以"_"或者"__"开头的函数,一般不在业务逻辑中调用,仅在底层中使用,以表示这些变量、函数非常特殊,使用时需要注意。

变量名

成员变量,小写加下划线,以下划线结尾。

非成员变量,小写加下划线

class CodeStyle
{
public:
    // 局部变量及函数参数都是小写加划线
    void set_mask(int mask)
    {
        int old_mask = style_mask_;
        style_mask_ = mask;
    }
    int style_mask_; // 成员变量,下划线开头
};

常量、宏定义

全大写加下划线

#define NDBG_MEM
#define MAX_CONNECTION 1024;
const static BUFFER_SIZE = 1024;
local BAG_SIZE = 1024

枚举

名字为大驼峰 枚举变量则为大写名字首字母加下划线开始,后续以全大写加下划线定义每个枚举值

// 名字为大驼峰
enum CodeStyleType
{
    // 大写名字首字母加下划线开始,后续以全大写加下划线定义每个枚举值
    CST_NONE      = 0,
    CST_PRETTY    = 1,
    CST_NO_INDENT = 2,

    CST_MAX
}

typedef

定义新类型时,与类名一致,大驼峰

typedef int CodeStyleMask;

覆盖其他类型时,不作要求

// 用std的string类型覆盖c的string(即char *)
typedef std::string string;

大括号及换行处理

大括号需要别起一行

if语句如果超过单行字符数限制,或者有else,则if语句必须换行。一旦换行,必须使用大括号

// 简单的语句,可以放一行
if (count > max) count = max;

// 放在一行太长了,则必须另起一行
// 大括号
if (count > max && -1 == option::AUTO_UPDATE_MAX)
{
    option::instance()->set_max(count);
}
else
{
    count = max;
}

缩写

缩写一般是命名太长了,取各个单词首字母,因此类使用全大写,变量名使用全小写

// InputOutput缩写为IO,仍然使用类名的命名方式,首字母大写
class IO
{
};

class *io = new IO(); // 变量名使用全小写,但无需用下划线

如果是一个单词缩写,仍然使用首字母大写

// Ctx是context的缩写,是一个单词
struct Ctx *ctx = NULL;

注释

  1. 函数、变量名的注释使用doxgen格式,这样在VS、Qt Creator等编辑器中才会有提示
  2. 仅在声明处注释,实现时不需要注释,这样不需要维护两份注释。要看的时候鼠标放上去就会显示声明处的注释
/**
 * doxgen的多行注释格式,很多编辑器都只会提示这种注释,/* ... 或者 // 这种是不提示的
 */
class Foo
{
public:
    /**
     * 函数的注释,合理使用@param和@return
     * @param i 输入的数值
     * @return 大小
     */
    int is(int i);
private:
    int _i; /// doxgen的单行注释,只有这个会有提示
};

// 函数的实现不需要再写一次注释,两份同样的注释不好维护
// 如果要写,也不要用doxgen格式的,不然编辑器会混淆
int Foo:is(int i)
{
    // 在函数体里,根据需求写注释,这时候用啥类型的注释格式都没有要求了
    return 0;
}
  1. lua也需要使用@param@return注释,部分插件是有提示的
-- 一般来讲lua只有实现没有声明,所以注释一般是写在实现这里
-- @param i 输入的数值
-- @return 返回数值大小
local function test(i)
end

使用框架定义好的数据类型

为了方便重构和平台迁移以及一些业务调整,框架会定义一些基础类型。写业务逻辑时必须要使用定义的类型

// 定义玩家id类型,必须要用Pid,不能直接用int32_t,避免以后修改为64位时需要多处修改
typedef int32_t Pid;

// 框架定义了基础数据类型 int32_t等,不要再使用int类型
int count = 0;

// long 32bit和64bit平台不一致,可能导致在不同平台出现问题
long max = 0;

参数或者多个变量、操作符用空格隔开

这个很多编辑器都会自动加空格,比如Visual Studio自动完成函数时,参数就自动加了空格。Sublime Text也会加。

// 多个参数时,逗号后面有空格
void add(int32_t src, int32_t dst);

// 多个变量时,逗号后面有空格
int32_t a, b, c;

// 操作符,运算符用空格隔开
int32_t x = a << 1;
int32_t y = a * b;

代码文件最后空一行

一些文本编辑器,在最后空一行有要求,而且空一行也比较好看。这个在编辑器设置一下就好,比如Visual Sutdio Code勾上Insert Final Newline即可

行尾不能有多余空格

一行结束的时候,即使是空行,也不要有多余的空格或者tab,这个在编辑器设置一下即可。比如Visual Sutdio Code勾上Trim Trailing Whitespace即可

一些不合理的代码风格

  1. 命名、大小写与框架不一致
  2. 混用tab、space缩进,缩进格子不统一
  3. 同一概念,不同命名 同一个概念的变量名,在该项目中应该都是统一命名的。这样可以大大减少他人理解代码的难度。例如:大家都把玩家对象命名为player,你把它命名为actor,那么另人看你的代码就更难理解。
  4. 一个函数代码量太大 行数太多,需要滚动页面才能看完。函数太复杂,看着看着就不记得某个变量在哪里声明了,要往回看
  5. 一行代码太长 通常一行代码不超过80个字符,显示器大可以设置为120。最低限度,不要让别人需要拖动编辑器的滚动条才能看完一行代码。特殊情况除外,比如lua中一行字符串不好换行。
  6. 没有处理异常 当程序发现不该出现的异常时,至少能以一种方式告诉程序员程序发生了异常,并且尽可能保证数据准确性。方式通常是assert、log...

直接返回某个值,上一层又未处理。事后又无法追溯异常,这都是不合格的代码 7. 重复的代码太多 如果相同的逻辑在多个地方出现,则应该把逻辑抽象成一个函数去调用。这个具体得看业务需求以及上线后的风险而定。

使用的语言版本

不同的语言版本(如Lua 5.1与Lua 5.3,C++03与C++11)之间存在较大差异。开发环境在部署后,这些就会确定下来,一般都使用新的版本,不用考虑兼容旧版本。因为游戏开发中,很少会有兼容老版本的需求。

项目中的规范

仅仅对代码来说,可以不断重构来让其很优雅、高效。但对一个项目来说,则需要考虑历史、时间、人员变动。旧的项目定义一些规范(甚至是习惯的写法),非极端情况下还是需要按旧规范来写的。一些好的规范可以在新项目或者项目重构时引入。

使用工具来做规范化

  • C++目录使用Clang-format来自动格式化C++代码,配置见.clang-format
  • Lua使用luacheck来自检错误

TODO

Lua暂时没有找到好用的代码格式化工具