From 08ad7783feb41024c7e1aa5ac8cc0dbb6ebd25c0 Mon Sep 17 00:00:00 2001 From: zoubing Date: Mon, 15 Jul 2024 16:41:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E7=9B=AE=E5=BD=95=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- llvm-coding-standards.md | 78 ++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/llvm-coding-standards.md b/llvm-coding-standards.md index 0305370..b4c72a1 100644 --- a/llvm-coding-standards.md +++ b/llvm-coding-standards.md @@ -63,7 +63,7 @@ - [Don’t Use Braces on Simple Single-Statement Bodies of if/else/loop Statements](#dont-use-braces-on-simple-single-statement-bodies-of-ifelseloop-statements) - [其它参考](#其它参考) -## 导论 +## 导论 This document describes coding standards that are used in the LLVM project. Although no coding standards should be regarded as absolute requirements to be followed in all instances, coding standards are particularly important for large-scale code bases that follow a library-based design (like LLVM). @@ -85,7 +85,7 @@ The ultimate goal of these guidelines is to increase the readability and maintai 这些准则的最终目标就是提高我们公共源码库的可读性和可维护性。 -## 语言、库和标准 +## 语言、库和标准 Most source code in LLVM and other LLVM projects using these coding standards is C++ code. There are some places where C code is used either due to environment restrictions, historical restrictions, or due to third-party source code imported into the tree. Generally, our preference is for standards conforming, modern, and portable C++ code as the implementation language of choice. @@ -141,17 +141,17 @@ Go code should strive to be idiomatic. Two good sets of guidelines for what this Go 代码应该尽量符合习惯。[Effective Go](https://golang.org/doc/effective_go.html) 和 [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) 是两套好的准则。 -## 机械的代码问题 +## 机械的代码问题 -### 代码格式化 +### 代码格式化 -#### 注释 +#### 注释 Comments are important for readability and maintainability. When writing comments, write them as English prose, using proper capitalization, punctuation, etc. Aim to describe what the code is trying to do and why, not how it does it at a micro level. Here are a few important things to document: 注释对于可读性和可维护性非常重要。当写注释时,使用适当的大小写、标点符号等将其写成英文句子。专注于描述代码试图做什么和为什么这么做,不要从微观的角度去描述代码做的事情。以下是一些重要的记录: -#### 头文件 +#### 头文件 Every source file should have a header on it that describes the basic purpose of the file. The standard header looks like this: @@ -188,13 +188,13 @@ The main body is a Doxygen comment (identified by the /// comment marker instead 主体部分是一些描述该文件用途的*Doxygen*注释(通过 `///` 注释记号而不是 `//`来标记)。第一句(段落以 `\brief`开始)用作于摘要。任何附加的信息都应该以空行分隔。如果一个算法是基于一篇论文或者在其它来源说明的,则提供一个引用。 -#### 类概述 +#### 类概述 Classes are a fundamental part of an object-oriented design. As such, a class definition should have a comment block that explains what the class is used for and how it works. Every non-trivial class is expected to have a doxygen comment block. 类是面向对象设计的基础部分。一个类定义应该有一个描述该类的用途和如何工作的注释块。每一个非平凡类都应该有一个*doxygen*注释块。 -#### 方法信息 +#### 方法信息 Methods and global functions should also be documented. A quick note about what it does and a description of the edge cases is all that is necessary here. The reader should be able to understand how to use interfaces without reading the code itself. @@ -204,7 +204,7 @@ Good things to talk about here are what happens when something unexpected happen 这里更值得讨论的事情是当意外出现时会发生什么,例如,方法是否返回null? -### 注释格式化 +### 注释格式化 In general, prefer C++-style comments (// for normal comments, /// for doxygen documentation comments). There are a few cases when it is useful to use C-style (/**/) comments however: @@ -244,7 +244,7 @@ Commenting out large blocks of code is discouraged, but if you really have to do 不推荐对大规模代码块进行注释,如果必须要注释的话(记录意图或者作为调试打印的建议),使用 `#if 0` 和 `#endif`。 适当的嵌套是比一般的使用C风格注释更好的行为。 -### 使用doxygen注释 +### 使用doxygen注释 Use the \file command to turn the standard file header into a file-level comment. @@ -336,7 +336,7 @@ void example(); void example() { ... } ``` -### 错误和警告消息 +### 错误和警告消息 Clear diagnostic messages are important to help users identify and fix issues in their inputs. Use succinct but correct English prose that gives the user the context needed to understand what went wrong. Also, to match error message styles commonly produced by other tools, start the first sentence with a lower-case letter, and finish the last sentence without a period, if it would end in one otherwise. Sentences which end with different punctuation, such as “did you forget ‘;’?”, should still do so. @@ -394,7 +394,7 @@ LLVM project and subproject headers should be grouped from most specific to leas LLVM 项目和子项目的头文件应该从最高优先级到最低优先级分组,理由同上。举个例子,LLDB 同时依赖 clang 和 LLVM,并且 clang 依赖 LLVM。所以一个 LLDB 源文件应该最先包含 LLDB ,其次是 clang 头文件,其次是 LLVM 头文件,这样做是为了在源文件中的头文件或更前的头文件包含情况下降低LLDB头文件包含缺失的可能。clang 同样的应该在包含LLVM文件之前包含其自身的头文件。这个规则适用于所有的 LLVM 的子项目。 -### 代码宽度 +### 代码宽度 Write your code to fit within 80 columns. @@ -404,7 +404,7 @@ There must be some limit to the width of the code in order to allow developers t 代码的宽度限制是为了允许开发者同时在一个窗口中并排打开多个文件而可以合适的显示。如果你打算选择一个宽度限制,这个限制是任意的但是你同样需要选择一个标准。选择90列代替80列并不增加任何有意义的值并且将对输出代码有坏处。此外,有非常多个其它项目对80列进行了标准化,因此有些人为它对编辑器做了配置(对比其它的宽度,例如90列)。 -### 空格 +### 空格 In all cases, prefer spaces to tabs in source files. People have different preferred indentation levels, and different styles of indentation that they like; this is fine. What isn’t fine is that different editors/viewers expand tabs out to different tab stops. This can cause your code to look completely unreadable, and it is not worth dealing with. @@ -418,7 +418,7 @@ Do not add trailing whitespace. Some common editors will automatically remove tr 不要在结尾添加空格,一些常用的编辑器会在保存文件时自动的移除结尾的空格,这样导致在diff或者commit时产生不相关的改变。 -#### 类代码块格式化lambda +#### 类代码块格式化lambda When formatting a multi-line lambda, format it like a block of code. If there is only one multi-line lambda in a statement, and there are no expressions lexically after it in the statement, drop the indent to the standard two space indent for a block of code, as if it were an if-block opened by the preceding part of the statement: @@ -458,7 +458,7 @@ dyn_switch(V->stripPointerCasts(), }); ``` -#### 花括号初始化列表 +#### 花括号初始化列表 Starting from C++11, there are significantly more uses of braced lists to perform initialization. For example, they can be used to construct aggregate temporaries in expressions. They now have a natural way of ending up nested within each other and within function calls in order to build up aggregates (such as option structs) from local variables. @@ -481,9 +481,9 @@ llvm::Constant *Mask[] = { 这个格式化方案同样让工具例如 clang-format 可以非常简单的预测、一致的自动格式化。 -### 语言和编译器问题 +### 语言和编译器问题 -#### 像错误一样对待编译警告 +#### 像错误一样对待编译警告 Compiler warnings are often useful and help improve the code. Those that are not useful, can be often suppressed with a small code change. For example, an assignment in the if condition is often a typo: @@ -505,7 +505,7 @@ if ((V = getValue())) { } ``` -#### 编写可移植的代码 +#### 编写可移植的代码 In almost all cases, it is possible to write completely portable code. When you need to rely on non-portable code, put it behind a well-defined and well-documented interface. @@ -519,7 +519,7 @@ That said, LLVM does make extensive use of a hand-rolled form of RTTI that use t 为了尽量减小代码和可执行文件的大小,LLVM 未使用异常或者RTTI(运行时信息,如 dynamic_cast<>)。也就是说,LLVM确实使用了大量的手动滚动的RTTI的形式,该形式使用了像 [isa<>,cast<> 和 dyn_cast<>](https://llvm.org/docs/ProgrammersManual.html#isa) 之类的模板。这种形式的RTTI是可选的并且可以[添加至任何类](https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html)中。 -#### 不要使用静态构造函数 +#### 不要使用静态构造函数 Static constructors and destructors (e.g., global variables whose types have a constructor or destructor) should not be added to the code base, and should be removed wherever possible. @@ -533,7 +533,7 @@ Static constructors have negative impact on launch time of programs that use LLV 将LLVM作为库使用时,静态构造函数在程序启动的时候具有负面的影响。我们真正希望将额外的LLVM目标或者其它库链接至应用程序中的开销为零,但是静态构造函数违背了这个目标。 -#### 使用class和struct关键字 +#### 使用class和struct关键字 In C++, the class and struct keywords can be used almost interchangeably. The only difference is when they are used to declare a class: class makes all members private by default while struct makes all members public by default. @@ -574,7 +574,7 @@ In C++, the class and struct keywords can be used almost interchangeably. The on }; ``` -#### 不要使用花括号初始化列表调用构造函数 +#### 不要使用花括号初始化列表调用构造函数 Starting from C++11 there is a “generalized initialization syntax” which allows calling constructors using braced initializer lists. Do not use these to call constructors with non-trivial logic or if you care that you’re calling some particular constructor. Those should look like function calls using parentheses rather than like aggregate initialization. Similarly, if you need to explicitly name the type and call its constructor to create a temporary, don’t use a braced initializer list. Instead, use a braced initializer list (without any type for temporaries) when doing aggregate initialization or something notionally equivalent. Examples: @@ -617,7 +617,7 @@ Similarly, C++14 adds generic lambda expressions where parameter types can be au 同样的,C++14 新增类型可以为auto的通用lambda表达式,可以在原本使用模板的地方使用它。 -#### 小心auto带来的不必要拷贝 +#### 小心auto带来的不必要拷贝 The convenience of auto makes it easy to forget that its default behavior is a copy. Particularly in range-based for loops, careless copies are expensive. @@ -640,7 +640,7 @@ for (const auto *Ptr : Container) { observe(*Ptr); } for (auto *Ptr : Container) { Ptr->change(); } ``` -#### 小心对指针排序带来的不确定性 +#### 小心对指针排序带来的不确定性 In general, there is no relative ordering among pointers. As a result, when unordered containers like sets and maps are used with pointer keys the iteration order is undefined. Hence, iterating such containers may result in non-deterministic code generation. While the generated code might work correctly, non-determinism can make it harder to reproduce bugs and debug the compiler. @@ -650,17 +650,17 @@ In case an ordered result is expected, remember to sort an unordered container b 如果期望结果有序,切记在迭代器之前对无序容器进行排序。或者你想迭代指针的key则使用顺序容器像 `vector/MapVector/SetVector`。 -#### 小心相同元素不确定的排序顺序 +#### 小心相同元素不确定的排序顺序 std::sort uses a non-stable sorting algorithm in which the order of equal elements is not guaranteed to be preserved. Thus using std::sort for a container having equal elements may result in non-deterministic behavior. To uncover such instances of non-determinism, LLVM has introduced a new llvm::sort wrapper function. For an EXPENSIVE_CHECKS build this will randomly shuffle the container before sorting. Default to using llvm::sort instead of std::sort. `std::sort` 使用了一个不稳定的排序算法,相同元素的顺序不能保证被保留下来。因此使用`std::sort`对一个具有相同元素的容器排序时可能出现不确定的行为。为了发现这些不确定的情况,LLVM 引入了一个新的 `llvm::sort` 封装函数。使用 `EXPENSIVE_CHECKS` 编译时在排序之前随机的打乱容器内的顺序。默认使用 `llvm::sort` 代替 `std::sort`。 -## 风格问题 +## 风格问题 -### 上层问题 +### 上层问题 -#### 头文件独立 +#### 头文件独立 Header files should be self-contained (compile on their own) and end in .h. Non-header files that are meant for inclusion should end in .inc and be used sparingly. @@ -678,7 +678,7 @@ In general, a header should be implemented by one or more .cpp files. Each of th 通常而言,一个头文件应该被一个或多个 `.cpp` 实现。每一个 `.cpp` 文件都应该首先包含定义接口的头文件。确保头文件所有的依赖都能够显式正确的添加至该头文件中。 -#### 库层次 +#### 库层次 A directory of header files (for example include/llvm/Foo) defines a library (Foo). Dependencies between libraries are defined by the LLVMBuild.txt file in their implementation (lib/Foo). One library (both its headers and implementation) should only use things from the libraries listed in its dependencies. @@ -706,7 +706,7 @@ It is easy to try to go too overboard on this recommendation, however. You must 这个建议很容易导致偏激的态度,然而,你**必须**包含所有正在使用的头文件,无论是直接还是间接从其它文件包含。为确保你不会意外的忘记在你的模块头文件中包含头文件,确认在实现文件中**第一个**包含你的模块头文件(就像上面提到的)。这样,你后面就会发现不会再有隐藏的依赖了。 -#### 保持私有的内部头文件 +#### 保持私有的内部头文件 Many modules have a complex implementation that causes them to use more than one implementation (.cpp) file. It is often tempting to put the internal communication interface (helper classes, extra functions, etc) in the public module header file. Don’t do this! @@ -911,7 +911,7 @@ The idea is to reduce indentation and the amount of code you have to keep track 这样做的目的是减少缩进和在阅读代码时需要跟踪的代码量。 -#### 将判断循环转换为判断函数 +#### 将判断循环转换为判断函数 It is very common to write small loops that just compute a boolean value. There are a number of ways that people commonly write these, but an example of this sort of thing is: @@ -953,9 +953,9 @@ There are many reasons for doing this: it reduces indentation and factors out co 这样做有很多理由:减少了缩进并且分离出可以让其它代码检测相同判断的共享代码。更重要的是,让你强制为这个函数起一个名字,并且强制让你再为它写上注释。在这个简短的例子中没有添加很多值,然而如果这个if条件非常复杂,读者通过这个判断可以非常容易地理解代码。而不是一开始就面对如何检测 `BarList` 中是否包含了 `foo` 的内联细节,我们可以信任这个函数名称并且继续在更好的位置进行阅读。 -### 底层问题 +### 底层问题 -#### 合适的类型、函数、变量和枚举命名 +#### 合适的类型、函数、变量和枚举命名 Poorly-chosen names can mislead the reader and cause bugs. We cannot stress enough how important it is to use descriptive names. Pick names that match the semantics and role of the underlying entities, within reason. Avoid abbreviations unless they are well known. After picking a good name, make sure to use consistent capitalization for the name, as inconsistency requires clients to either memorize the APIs or to look it up to find the exact spelling. @@ -1009,7 +1009,7 @@ Vehicle makeVehicle(VehicleType Type) { } ``` -#### 善用断言 +#### 善用断言 Use the “assert” macro to its fullest. Check all of your preconditions and assumptions, you never know when a bug (not necessarily even yours) might be caught early by an assertion, which reduces debugging time dramatically. The “” header file is probably already included by the header files you are using, so it doesn’t cost anything to use it. @@ -1109,7 +1109,7 @@ The exception to the general rule (i.e. it’s not an exception for the std name 一般规则的例外(`std`命名空间不是例外)是实现文件。打个比方,LLVM项目中所有实现的代码都位于llvm命名空间中。所以在`.cpp`文件include之后使用 `using namespace std;`指令是可行的,且实际上更清晰。在基于花括号缩进的源码编辑器可以减少主体的缩进,并且让上下文在概念上更加清晰。此规则的一般形式是,在任何命名空间内实现代码的任何`.cpp`文件都可以使用该命名空间(及其父命名空间),但不应该使用其它的命名空间。 -#### 为头文件中的类提供虚函数锚 +#### 为头文件中的类提供虚函数锚 If a class is defined in a header file and has a vtable (either it has virtual methods or it derives from classes with virtual methods), it must always have at least one out-of-line virtual method in the class. Without this, the compiler will copy the vtable and RTTI into every .o file that #includes the header, bloating .o file sizes and increasing link times. @@ -1125,7 +1125,7 @@ A knock-on effect of this stylistic requirement is that when building LLVM with 这种风格上的需求带来一系列连锁反应是当使用 GCC 编译 LLVM 时,如果你从每种枚举都覆盖的case中返回,可能得到 "control may reach end of non-void function" 相关的警告,这是因为 GCC 假设枚举表达式可以取任何可以表达的值,而不仅仅是那些独立的枚举值。在switch后使用 `llvm_unreachable`来消除这些警告。(感觉有点像是在黑GCC) -#### 尽可能的使用基于范围的循环 +#### 尽可能的使用基于范围的循环 The introduction of range-based for loops in C++11 means that explicit manipulation of iterators is rarely necessary. We use range-based for loops wherever possible for all newly added code. For example: @@ -1240,13 +1240,13 @@ public: }; ``` -### 微观细节 +### 微观细节 This section describes preferred low-level formatting guidelines along with reasoning on why we prefer them. 这个部分描述了推荐低级的格式化规则及推荐它们的理由。 -#### 括号之前的空格 +#### 括号之前的空格 Put a space before an open parenthesis only in control flow statements, but not in normal function call expressions and function-like macros. For example: @@ -1267,7 +1267,7 @@ The reason for doing this is not completely arbitrary. This style makes control 这样做的理由并不是任意的,这种格式让控制流操作符更加突出并且让表达式更流畅。 -#### 推荐前置自增 +#### 推荐前置自增 Hard fast rule: Preincrement (++X) may be no slower than postincrement (X++) and could very well be a lot faster than it. Use preincrementation whenever possible. @@ -1450,7 +1450,7 @@ if (auto *D = dyn_cast(D)) { } ``` -## 其它参考 +## 其它参考 A lot of these comments and recommendations have been culled from other sources. Two particularly important books for our work are: