🔗 代码风格指南
标记为 ENFORCED 的详细信息将由源代码测试机制进行检查和强制执行。
🔗 C++ 指南
🔗 源代码格式化指南
- 我们有一个 astyle 包装器,可以格式化代码而不破坏它。
- 如果您有 astyle 版本 3.1,请使用 ~/scripts/formater.pl 格式化您的更改。
- 此格式化程序会定期对整个代码运行,以强制执行某些指南,但在提交代码时,如果您的代码符合这些指南,将有助于减少麻烦。
🔗 强制执行的规则
- 4 个空格缩进,不使用制表符
- 任何行的末尾不允许有空格
- 不允许出现多行空行。一行就足以分隔内容。
- if 语句及其参数 () 括号之间有一个空格
- 构造体的开花括号 { 与其构造体(if, while)开始在同一行
- 在条件语句中,赋值必须用 ( 花括号括起来:
if ((a = b))...,但局部变量声明和初始化则不需要:if (T a = b)...。
该格式化程序有时会强制执行一些奇怪的缩进。尤其是在
#if ... #endif指令之后。如果遇到这些情况,请暂时忽略。它们将在格式化程序的后续版本中得到纠正。
🔗 强制编码规则
- 记录(至少简要记录)每个新的类型、类、成员或全局变量。推荐使用 Doxygen 格式。
- 五大成员(三大加两小)
- 三大成员:拷贝构造函数、析构函数和赋值运算符
- 移动成员:移动构造函数和移动赋值运算符
- 如果一个类在没有五大成员的情况下工作良好,则不要定义任何五大成员。
- 如果你必须定义五大成员中的一个,则需要声明全部五大成员。
- 如果类有一个非默认的析构函数,您可以决定定义一个移动成员。如果您定义了一个移动成员,也请声明另一个移动成员。
- 尽可能使用
= default声明,如果它足够且= delete声明,则表示该方法被禁止。
- 按照下面的章节的详细说明,避免添加新的全局变量。
- 命名约定(参见 Features/SourceLayout)将被使用。
🔗 规则:禁止新增全局变量
C++ 全局变量容易出现各种 初始化顺序相关问题。在大多数情况下,全局变量是不必要的。不应在 Squid 中添加不必要的全局变量。在大多数情况下,一个类型为 Type 的不必要全局变量 Foo 应该被替换为以下包装函数
auto &
Foo()
{
static const auto foo = new Type(...);
return *foo;
}
该函数可以标记为 static,成为类成员,调整为返回常量引用,和/或包含更复杂的对象初始化代码,视情况而定。
通过函数包装器访问对象(而不是直接访问全局对象)的性能成本增加不是避免全局变量的有效借口。
此规则适用于所有(或可能成为)易受初始化顺序问题影响的新对象,包括全局命名空间中的全局变量、命名空间作用域的全局变量以及类作用域的静态成员(无论其访问修饰符如何)。此规则不适用于函数作用域的变量。
此规则确实适用于内置/内在类型的潜在全局变量,因为它们可能受到类似的 初始化问题。
此规则不适用于现有全局变量。全局变量的先前存在由其名称确定。例如,更改现有全局变量的类型不会自动使该全局变量受此规则的约束,尤其是当该全局变量的许多用户保持不变时。当然,一个专门的拉取请求可以提议转换现有全局变量,如果作者认为转换的优点大于其负面影响。
为避免 反初始化顺序问题,包装函数必须动态分配潜在的全局变量以防止其被销毁。在极少数情况下,如果全局变量必须在程序结束时销毁,则可以使用 Nifty Counter 代替包装函数。
🔗 建议的编码规则
- 使用内部一致的命名方案(具体选择请参见下文)。
- 全局名称和所有类型名称中的单词采用 CamelCase 风格大写
- 包括第一个单词。
- 缩略词应小写以适应(例如 Http)
- 这包括命名空间、类类型、全局变量、静态类成员和宏
- 尽可能使用 const 限定符进行声明
- 使用 bool 作为布尔类型
- 避免使用宏
- 名称不以下划线开头
- 成员名称以下划线结尾。除非名称与方法名称冲突
- 对递增和递减运算符使用前缀形式(例如
++c而不是c++) - 当一个方法被继承并在本地重载时,它应该被分组在一个单行注释下,并命名其来源的 API。
🔗 单词大写示例
namespace Foo // namespace name CamelCased
{
class ClassStats; // class type name CamelCased
class ClassName
{
public:
static ClassStats &Stats(); // static methods use CameCased
void clear();
private:
static ClassStats Stats_; // static member CamelCased. Underscore since name collides with Stats() method
int internalState;
};
extern void ReportUsage(ostream &); // global function CamelCased
🔗 类声明布局
class Foo : public Bar
{
CBDATA_* or MEMPROXY_* special macro.
public:
all public typedef
all constructors and operators
Foo destructor (if any)
/* Bar API */
all methods overloaded from FooBar parent class
all public static methods
all public member methods
all public static variables
all public member variables
protected:
all protected static methods
all protected member methods
all protected static variables
all protected member variables
private:
all private static methods
all private member methods
all private static variables
all private member variables
};
🔗 成员命名
选择下面描述的适用样式之一并坚持使用。对于旧类,尝试选择与正在使用的样式更接近的样式。
-
- 访问器
- 显式 set, get, has
void setMember(const Member &); const Member &getMember() const; // may also return a copy Member &getMember(); bool hasMember() const;或 紧凑型
void member(const Member &); const Member &member() const; // may also return a copy Member &member(); bool hasMember() const;
-
- 数据成员
- 对于公共数据成员,不使用下划线后缀。对布尔成员使用动词前缀。
int counter; int next; bool isClean; bool sawHeader; - 对于受保护和私有数据成员:可以使用下划线后缀来强调该数据成员不是公共的,并且如果数据成员名称否则会与方法名称冲突,则必须使用下划线后缀。对布尔成员使用动词前缀。
int counter_; int next_; bool isClean_; bool sawHeader_;
-
状态检查
- 以适当的动词为前缀: is, has/have, can
bool canVerb() const; bool hasNoun() const; bool haveNoun() const; // if class name is plural bool isAdjective() const; // but see below -
避免使用否定词,因为 if 语句中的双重否定会令人困惑;让调用者在需要时进行否定。
bool notAdjective() const; // XXX: avoid due to !notAdjective() - 动词 is 可以省略,尤其是当结果不会与命令混淆时:当 is 后面的形容词可以解释为动词时,就会发生混淆。
bool isAtEnd() const; // OK, but excessive bool atEnd() const; // OK, no confusion bool isFull() const; // OK, but excessive bool full() const; // OK, no confusion bool clear() const; // XXX: may look like a command to clear state bool empty() const; // XXX: may look like a "become empty" command - 以适当的动词为前缀: is, has/have, can
🔗 文件 #include 指南
- 最小系统包含
- Squid 提供的自定义头文件
- 将内部头文件包含放在系统头文件之上
- 省略包装器
- 始终使用双引号 (“”) 包含
- 强制执行:按字母顺序排序
- 使用完整路径(只有 src/ 前缀可以省略)
- 系统 C++ 头文件(不带任何扩展名后缀)
- 始终使用 <> 包含
- 省略任何 HAVE_ 包装器
- 按字母顺序排序
- 如果文件不可移植,请勿使用它
- 系统 C 头文件(带 .h 后缀)
- 始终使用 <> 包含
- 强制使用 HAVE_FOO_H 包装器
- 在 C++ 替代可用时避免使用
- 按字母顺序排序
- 应通过 libcompat 导入依赖顺序的头文件
强制执行
- 按字母顺序对内部包含进行排序
.cc 文件仅
- 将 squid.h 作为第一个包含文件。
.h 和 .cci 文件
- 不要包含 squid.h
布局示例
// local includes sorted alphabetically with squid.h first
#include "squid.h"
#include "comm/forward.h"
#include "local.h"
// system C++ includes alphabetically sorted and not-wrapped
#include <cstdlib>
#include <iostream>
// System C includes alphabetically sorted and wrapped
#if HAVE_ACCESS_H
#include <access.h>
#endif
#if HAVE_GETOPT_H
#include <getopt.h>
#endif
🔗 C++ 中的组件宏
Squid 使用 autoconf 定义的宏在构建时消除实验性或可选组件。
- C++ 代码中的名称应以 USE_ 开头
- 使用 #if 和 #if ! 进行测试,而不是 #ifdef 或 #ifndef
- 应将所有仅与组件相关的代码包装起来;包括编译器指令和 #include 语句
强制执行:
- 必须在 .h 文件中使用以包装相关代码。