Merge branch 'docs/translate_style_guide' into 'master'

docs: Provide CN translation for style-guide

Closes DOC-8255

See merge request espressif/esp-idf!31886
This commit is contained in:
Shen Meng Jing 2024-09-03 21:11:01 +08:00
commit 1611d1344f
2 changed files with 621 additions and 59 deletions

View File

@ -1,13 +1,14 @@
Espressif IoT Development Framework Style Guide
===============================================
:link_to_translation:`zh_CN:[中文]`
About This Guide
----------------
Purpose of this style guide is to encourage use of common coding practices within the ESP-IDF.
Style guide is a set of rules which are aimed to help create readable, maintainable, and robust code. By writing code which looks the same way across the code base we help others read and comprehend the code. By using same conventions for spaces and newlines we reduce chances that future changes will produce huge unreadable diffs. By following common patterns for module structure and by using language features consistently we help others understand code behavior.
Style guide is a set of rules which are aimed to help create readable, maintainable, and robust code. By writing code which looks the same way across the code base, we help others read and comprehend the code. By using same conventions for spaces and newlines, we reduce chances that future changes will produce huge unreadable diffs. By following common patterns for module structure and by using language features consistently, we help others understand code behavior.
We try to keep rules simple enough, which means that they can not cover all potential cases. In some cases one has to bend these simple rules to achieve readability, maintainability, or robustness.
@ -24,29 +25,29 @@ Naming
^^^^^^
* Any variable or function which is only used in a single source file should be declared ``static``.
* Public names (non-static variables and functions) should be namespaced with a per-component or per-unit prefix, to avoid naming collisions. ie ``esp_vfs_register()`` or ``esp_console_run()``. Starting the prefix with ``esp_`` for Espressif-specific names is optional, but should be consistent with any other names in the same component.
* Public names (non-static variables and functions) should be namespaced with a per-component or per-unit prefix, to avoid naming collisions, e.g., ``esp_vfs_register()`` or ``esp_console_run()``. Starting the prefix with ``esp_`` for Espressif-specific names is optional, but should be consistent with any other names in the same component.
* Static variables should be prefixed with ``s_`` for easy identification. For example, ``static bool s_invert``.
* Avoid unnecessary abbreviations (ie shortening ``data`` to ``dat``), unless the resulting name would otherwise be very long.
* Avoid unnecessary abbreviations (e.g., shortening ``data`` to ``dat``), unless the resulting name would otherwise be very long.
Indentation
^^^^^^^^^^^
Use 4 spaces for each indentation level. Do not use tabs for indentation. Configure the editor to emit 4 spaces each time you press tab key.
Use four spaces for each indentation level. Do not use tabs for indentation. Configure the editor to emit four spaces each time you press tab key.
Vertical Space
^^^^^^^^^^^^^^
Place one empty line between functions. Do not begin or end a function with an empty line.
::
Place one empty line between functions. Do not begin or end a function with an empty line:
.. code-block:: c
void function1()
{
do_one_thing();
do_another_thing();
// INCORRECT, do not place empty line here
// INCORRECT, do not place an empty line here
}
// place empty line here
// place an empty line here
void function2()
{
// INCORRECT, do not use an empty line here
@ -61,7 +62,9 @@ The maximum line length is 120 characters as long as it does not seriously affec
Horizontal Space
^^^^^^^^^^^^^^^^
Always add single space after conditional and loop keywords::
- Always add single space after conditional and loop keywords:
.. code-block:: c
if (condition) { // correct
// ...
@ -76,7 +79,9 @@ Always add single space after conditional and loop keywords::
// ...
}
Add single space around binary operators. No space is necessary for unary operators. It is okay to drop space around multiply and divide operators::
- Add single space around binary operators. No space is necessary for unary operators. It is okay to drop space around multiply and divide operators:
.. code-block:: c
const int y = y0 + (x - x0) * (y1 - y0) / (x1 - x0); // correct
@ -87,18 +92,18 @@ Add single space around binary operators. No space is necessary for unary operat
const int y = y0+(x-x0)*(y1-y0)/(x1-x0); // INCORRECT
No space is necessary around ``.`` and ``->`` operators.
- Sometimes adding horizontal space within a line can help make code more readable. For example, you can add space to align function arguments:
Sometimes adding horizontal space within a line can help make code more readable. For example, you can add space to align function arguments::
.. code-block:: c
esp_rom_gpio_connect_in_signal(PIN_CAM_D6, I2S0I_DATA_IN14_IDX, false);
esp_rom_gpio_connect_in_signal(PIN_CAM_D7, I2S0I_DATA_IN15_IDX, false);
esp_rom_gpio_connect_in_signal(PIN_CAM_HREF, I2S0I_H_ENABLE_IDX, false);
esp_rom_gpio_connect_in_signal(PIN_CAM_PCLK, I2S0I_DATA_IN15_IDX, false);
Note however that if someone goes to add new line with a longer identifier as first argument (e.g., ``PIN_CAM_VSYNC``), it will not fit. So other lines would have to be realigned, adding meaningless changes to the commit.
Note however that if someone goes to add a new line with a longer identifier as first argument (e.g., ``PIN_CAM_VSYNC``), it will not fit. So other lines would have to be realigned, adding meaningless changes to the commit.
Therefore, use horizontal alignment sparingly, especially if you expect new lines to be added to the list later.
@ -106,11 +111,12 @@ Never use TAB characters for horizontal alignment.
Never add trailing whitespace at the end of the line.
Braces
^^^^^^
- Function definition should have a brace on a separate line::
- Function definition should have a brace on a separate line:
.. code-block:: c
// This is correct:
void function(int arg)
@ -123,7 +129,9 @@ Braces
}
- Within a function, place opening brace on the same line with conditional and loop statements::
- Within a function, place opening brace on the same line with conditional and loop statements:
.. code-block:: c
if (condition) {
do_one();
@ -139,7 +147,9 @@ Use ``//`` for single line comments. For multi-line comments it is okay to use e
Although not directly related to formatting, here are a few notes about using comments effectively.
- Do not use single comments to disable some functionality::
- Do not use single comment to disable some functionality:
.. code-block:: c
void init_something()
{
@ -148,7 +158,9 @@ Although not directly related to formatting, here are a few notes about using co
start_timer();
}
- If some code is no longer required, remove it completely. If you need it you can always look it up in git history of this file. If you disable some call because of temporary reasons, with an intention to restore it in the future, add explanation on the adjacent line::
- If some code is no longer required, remove it completely. If you need it, you can always look it up in git history of this file. If you disable some call because of temporary reasons, with an intention to restore it in the future, add explanation on the adjacent line:
.. code-block:: c
void init_something()
{
@ -160,7 +172,9 @@ Although not directly related to formatting, here are a few notes about using co
- Same goes for ``#if 0 ... #endif`` blocks. Remove code block completely if it is not used. Otherwise, add comment explaining why the block is disabled. Do not use ``#if 0 ... #endif`` or comments to store code snippets which you may need in the future.
- Do not add trivial comments about authorship and change date. You can always look up who modified any given line using git. E.g., this comment adds clutter to the code without adding any useful information::
- Do not add trivial comments about authorship and change date. You can always look up who modified any given line using git. For example, this comment adds clutter to the code without adding any useful information:
.. code-block:: c
void init_something()
{
@ -178,17 +192,17 @@ Line Endings
Commits should only contain files with LF (Unix style) endings.
Windows users can configure git to check out CRLF (Windows style) endings locally and commit LF endings by setting the ``core.autocrlf`` setting. `Github has a document about setting this option <github-line-endings>`.
Windows users can configure git to check out CRLF (Windows style) endings locally and commit LF endings by setting the ``core.autocrlf``. Github has a `document <https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings?platform=windows>`_ about setting this option .
If you accidentally have some commits in your branch that add LF endings, you can convert them to Unix by running this command in an MSYS2 or Unix terminal (change directory to the IDF working directory and check the correct branch is currently checked out, beforehand):
If you accidentally have some commits in your branch that add LF endings, you can convert them to Unix by running this command in an MSYS2 or Unix terminal (change directory to the IDF working directory and ensure that the correct branch is currently checked out, beforehand):
.. code-block:: bash
git rebase --exec 'git diff-tree --no-commit-id --name-only -r HEAD | xargs dos2unix && git commit -a --amend --no-edit --allow-empty' master
git rebase --exec 'git diff-tree --no-commit-id --name-only -r HEAD | xargs dos2unix && git commit -a --amend --no-edit --allow-empty' master
(Note that this line rebases on master, change the branch name at the end to rebase on another branch.)
Note that this line rebases on master, change the branch name at the end to rebase on another branch.
For updating a single commit, it is possible to run ``dos2unix FILENAME`` and then run ``git commit --amend``
For updating a single commit, it is possible to run ``dos2unix FILENAME`` and then run ``git commit --amend``.
Formatting Your Code
^^^^^^^^^^^^^^^^^^^^
@ -211,14 +225,18 @@ Alternatively, you can run ``astyle_py`` manually. You can install it with ``pip
Type Definitions
^^^^^^^^^^^^^^^^
Should be snake_case, ending with _t suffix::
Should be ``snake_case``, ending with ``_t`` suffix:
.. code-block:: c
typedef int signed_32_bit_t;
Enum
^^^^
Enums should be defined through the `typedef` and be namespaced::
Enums should be defined through the `typedef` and be namespaced:
.. code-block:: c
typedef enum
{
@ -233,55 +251,67 @@ Enums should be defined through the `typedef` and be namespaced::
Assertions
^^^^^^^^^^
The standard C ``assert()`` function, defined in ``assert.h`` should be used to check conditions that should be true in source code. In the default configuration, an assert condition that returns ``false`` or 0 will call ``abort()`` and trigger a :doc:`Fatal Error </api-guides/fatal-errors>`.
The standard C ``assert()`` function, defined in ``assert.h`` should be used to check conditions that should be true in source code. In the default configuration, an assert condition that returns ``false`` or ``0`` will call ``abort()`` and trigger a :doc:`Fatal Error </api-guides/fatal-errors>`.
``assert()`` should only be used to detect unrecoverable errors due to a serious internal logic bug or corruption, where it is not possible for the program to continue. For recoverable errors, including errors that are possible due to invalid external input, an :doc:`error value should be returned </api-guides/error-handling>`.
.. note::
When asserting a value of type ``esp_err_t`` is equal to ``ESP_OK``, use the :ref:`esp-error-check-macro` instead of an ``assert()``.
When asserting that a value of type ``esp_err_t`` is equal to ``ESP_OK``, use the :ref:`esp-error-check-macro` instead of an ``assert()``.
It is possible to configure ESP-IDF projects with assertions disabled (see :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL`). Therefore, functions called in an ``assert()`` statement should not have side-effects.
It is also necessary to use particular techniques to avoid "variable set but not used" warnings when assertions are disabled, due to code patterns such as::
It is also necessary to use particular techniques to avoid "variable set but not used" warnings when assertions are disabled, due to code patterns such as:
int res = do_something();
assert(res == 0);
.. code-block:: c
int res = do_something();
assert(res == 0);
Once the ``assert`` is optimized out, the ``res`` value is unused and the compiler will warn about this. However the function ``do_something()`` must still be called, even if assertions are disabled.
When the variable is declared and initialized in a single statement, a good strategy is to cast it to ``void`` on a new line. The compiler will not produce a warning, and the variable can still be optimized out of the final binary::
When the variable is declared and initialized in a single statement, a good strategy is to cast it to ``void`` on a new line. The compiler will not produce a warning, and the variable can still be optimized out of the final binary:
int res = do_something();
assert(res == 0);
(void)res;
.. code-block:: c
If the variable is declared separately, for example if it is used for multiple assertions, then it can be declared with the GCC attribute ``__attribute__((unused))``. The compiler will not produce any unused variable warnings, but the variable can still be optimized out::
int res = do_something();
assert(res == 0);
(void)res;
int res __attribute__((unused));
If the variable is declared separately, for example if it is used for multiple assertions, then it can be declared with the GCC attribute ``__attribute__((unused))``. The compiler will not produce any unused variable warnings, but the variable can still be optimized out:
res = do_something();
assert(res == 0);
.. code-block:: c
res = do_something_else();
assert(res != 0);
int res __attribute__((unused));
res = do_something();
assert(res == 0);
res = do_something_else();
assert(res != 0);
Header File Guards
------------------
All public facing header files should have preprocessor guards. A pragma is preferred::
All public facing header files should have preprocessor guards. A pragma is preferred:
.. code-block:: c
#pragma once
over the following pattern::
over the following pattern:
.. code-block:: c
#ifndef FILE_NAME_H
#define FILE_NAME_H
...
#endif // FILE_NAME_H
In addition to guard macros, all C header files should have ``extern "C"`` guards to allow the header to be used from C++ code. Note that the following order should be used: ``pragma once``, then any ``#include`` statements, then ``extern "C"`` guards::
In addition to guard macros, all C header files should have ``extern "C"`` guards to allow the header to be used from C++ code. Note that the following order should be used: ``pragma once``, then any ``#include`` statements, then ``extern "C"`` guards:
.. code-block:: c
#pragma once
@ -304,8 +334,8 @@ Include Statements
When writing ``#include`` statements, try to maintain the following order:
* C standard library headers.
* Other POSIX standard headers and common extensions to them (such as ``sys/queue.h``.)
* Common IDF headers (``esp_log.h``, ``esp_system.h``, ``esp_timer.h``, ``esp_sleep.h``, etc.)
* Other POSIX standard headers and common extensions to them (such as ``sys/queue.h``).
* Common IDF headers (``esp_log.h``, ``esp_system.h``, ``esp_timer.h``, ``esp_sleep.h``, etc).
* Headers of other components, such as FreeRTOS.
* Public headers of the current component.
* Private headers.
@ -322,7 +352,8 @@ The same rules as for C apply. Where they are not enough, apply the following ru
File Naming
^^^^^^^^^^^^
C++ Header files have the extension ``.hpp``. C++ source files have the extension ``.cpp``. The latter is important for the compiler to distinguish them from normal C source files.
C++ header files have the extension ``.hpp``. C++ source files have the extension ``.cpp``. The latter is important for the compiler to distinguish them from normal C source files.
Naming
^^^^^^
@ -330,10 +361,11 @@ Naming
* **Class and struct** names shall be written in ``CamelCase`` with a capital letter as beginning. Member variables and methods shall be in ``snake_case``. An exception from ``CamelCase`` is if the readability is severely decreased, e.g., in ``GPIOOutput``, then an underscore ``_`` is allowed to make it more readable: ``GPIO_Output``.
* **Namespaces** shall be in lower ``snake_case``.
* **Templates** are specified in the line above the function declaration.
* Interfaces in terms of Object-Oriented Programming shall be named without the suffix ``...Interface``. Later, this makes it easier to extract interfaces from normal classes and vice versa without making a breaking change.
* Interfaces in terms of object-oriented programming (OOP) shall be named without the suffix ``...Interface``. Later, this makes it easier to extract interfaces from normal classes and vice versa without making a breaking change.
Member Order in Classes
^^^^^^^^^^^^^^^^^^^^^^^
In order of precedence:
* First put the public members, then the protected, then private ones. Omit public, protected or private sections without any members.
@ -341,7 +373,7 @@ In order of precedence:
For example:
::
.. code-block:: cpp
class ForExample {
public:
@ -372,7 +404,8 @@ Spacing
Simple Example
^^^^^^^^^^^^^^^
::
.. code-block:: cpp
// file spaceship.h
#ifndef SPACESHIP_H_
@ -447,9 +480,7 @@ CMake Code Style
----------------
- Indent with four spaces.
- Maximum line length 120 characters. When splitting lines, try to
focus on readability where possible (for example, by pairing up
keyword/argument pairs on individual lines).
- Maximum line length 120 characters. When splitting lines, try to focus on readability where possible (for example, by pairing up keyword/argument pairs on individual lines).
- Do not put anything in the optional parentheses after ``endforeach()``, ``endif()``, etc.
- Use lowercase (``with_underscores``) for command, function, and macro names.
- For locally scoped variables, use lowercase (``with_underscores``).
@ -459,14 +490,14 @@ CMake Code Style
Configuring the Code Style for a Project Using EditorConfig
-----------------------------------------------------------
EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles. EditorConfig files are easily readable and they work nicely with version control systems.
EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles. EditorConfig files are easy to read and they work nicely with version control systems.
For more information, see `EditorConfig <https://editorconfig.org>`_ Website.
Third Party Component Code Styles
---------------------------------
ESP-IDF integrates a number of third party components where these components may have differing code styles.
ESP-IDF integrates a number of third party components, which may have different code styles.
FreeRTOS
^^^^^^^^
@ -475,8 +506,8 @@ The code style adopted by FreeRTOS is described in the `FreeRTOS style guide <ht
If a FreeRTOS source file is modified, the updated file can be formatted again by following the steps below:
1. Ensure that Uncrustify (v0.69.0) is installed on your system
2. Run the following command on the update FreeRTOS source file (where ``source.c`` is the path to the source file that requires formatting).
1. Ensure that Uncrustify (v0.69.0) is installed on your system.
2. Run the following command on the updated FreeRTOS source file (where ``source.c`` is the path to the source file that requires formatting).
.. code-block:: bash

View File

@ -1 +1,532 @@
.. include:: ../../en/contribute/style-guide.rst
乐鑫 IoT 开发框架风格指南
=========================
:link_to_translation:`en:[English]`
关于本指南
----------
本风格指南旨在鼓励遵循 ESP-IDF 中通用的编码规范。
风格指南提供了一系列规则,从而帮助创建可读性强、可维护性好且稳健的代码。编写与代码库风格一致的代码,可以促进阅读和理解;遵循相同的空格和换行规范,后期更改就不太会产生影响阅读的巨大差异;使用模块结构的通用模板并使用一致的编程语言特性,可以更好地理解代码行为。
本指南尽可能使规则保持简单,因此无法涵盖所有情况。某些时候,这些规则需要为代码的可读性、可维护性或稳健性让步。
对 ESP-IDF 中使用的第三方代码进行修改时,请遵循该特定项目的编写方式,提出有用的更改,以合并到上游项目中。
C 语言代码格式化
----------------
.. highlight:: c
.. _style-guide-naming:
命名
^^^^
* 任何仅在单个源文件中使用的变量或函数应声明为 ``static``
* 公共名称(非静态变量和函数)应使用每个组件或每个单元的前缀进行命名,以避免产生命名冲突,例如 ``esp_vfs_register()````esp_console_run()``。可以选择 ``esp_`` 作为乐鑫特定名称的前缀,但应与同一组件中的其他名称保持一致。
* 为了便于识别,静态变量应以 ``s_`` 为前缀。例如,``static bool s_invert``
* 除非名称会非常长,否则避免使用不必要的缩写(例如,将 ``data`` 缩写为 ``dat``)。
缩进
^^^^
每个缩进层级使用四个空格。不要使用制表符进行缩进。配置编辑器,确保在每次按下制表键时能打出四个空格。
垂直空格
^^^^^^^^
函数之间放置一行空行。不要在函数的开头或结尾处放置空行:
.. code-block:: c
void function1()
{
do_one_thing();
do_another_thing();
// 错误,请勿在此处使用空行
}
// 请在此处使用空行
void function2()
{
// 错误,请勿在此处使用空行
int var = 0;
while (var < SOME_CONSTANT) {
do_stuff(&var);
}
}
每行最多包含 120 个字符,只要不会严重影响可读性即可。
水平空格
^^^^^^^^
- 记得在条件和循环关键字后加一个空格:
.. code-block:: c
if (condition) { // 正确
// ...
}
switch (n) { // 正确
case 0:
// ...
}
for(int i = 0; i < CONST; ++i) { // 错误
// ...
}
- 在二元运算符前后加一个空格,一元运算符则不需要空格。乘法和除法运算符前后可省略空格:
.. code-block:: c
const int y = y0 + (x - x0) * (y1 - y0) / (x1 - x0); // 正确
const int y = y0 + (x - x0)*(y1 - y0)/(x1 - x0); // 正确
int y_cur = -y; // 正确
++y_cur;
const int y = y0+(x-x0)*(y1-y0)/(x1-x0); // 错误
``.````->`` 运算符前后不需要加空格。
- 有时在一行中添加水平空格可以提高代码的可读性。例如,可以添加空格以对齐函数参数:
.. code-block:: c
esp_rom_gpio_connect_in_signal(PIN_CAM_D6, I2S0I_DATA_IN14_IDX, false);
esp_rom_gpio_connect_in_signal(PIN_CAM_D7, I2S0I_DATA_IN15_IDX, false);
esp_rom_gpio_connect_in_signal(PIN_CAM_HREF, I2S0I_H_ENABLE_IDX, false);
esp_rom_gpio_connect_in_signal(PIN_CAM_PCLK, I2S0I_DATA_IN15_IDX, false);
但是请注意,如果新写一行,将较长的标识符作为第一个参数(例如,``PIN_CAM_VSYNC``),这条规则将不再适用,其他行也不得不重新进行对齐,提交记录中将出现无意义的更改。
因此,请谨慎使用水平对齐,尤其是在后期可能会向列表中添加新行的情况下。
切勿使用制表符进行水平对齐。
切勿在行末以空格结尾。
大括号
^^^^^^
- 函数定义应将大括号放在单独的一行:
.. code-block:: c
// 这是正确的:
void function(int arg)
{
}
// 这是错误的:
void function(int arg) {
}
- 在函数内部,请将左大括号、条件及循环语句放在同一行:
.. code-block:: c
if (condition) {
do_one();
} else if (other_condition) {
do_two();
}
注释
^^^^
使用 ``//`` 进行单行注释;多行注释既可以在每行使用 ``//``,也可以使用 ``/* */`` 块。
尽管与格式无直接关系,但以下是一些关于如何有效使用注释的注意事项。
- 不要使用单行注释来禁用某些功能:
.. code-block:: c
void init_something()
{
setup_dma();
// load_resources(); // 读者会疑惑为什么此处被置于注释中。
start_timer();
}
- 如果不再需要某些代码,请将其完全删除。这些代码之后可以随时在相关文件的 git 历史记录中查到。如果出于某些临时原因禁用了部分函数调用,并且打算之后将其恢复,请在相邻行添加解释:
.. code-block:: c
void init_something()
{
setup_dma();
// TODO: 此处应加载资源,但加载器尚未完全集成。
// load_resources();
start_timer();
}
- 上述规则同样适用于 ``#if 0 ... #endif`` 块。如果不再需要代码块,请将其完全删除。否则,请添加注释以说明禁用该代码块的原因。请不要使用 ``#if 0 ... #endif`` 或注释来存储将来可能需要的代码片段。
- 不要添加有关作者和更改日期的琐碎注释,可以直接使用 git 来查找修改人及修改内容等相关信息。例如,类似下文的注释不仅没添加任何有用的信息,还会使代码变得杂乱:
.. code-block:: c
void init_something()
{
setup_dma();
// XXX 于 2016-09-01 添加
init_dma_list();
fill_dma_item(0);
// XXX 结束添加
start_timer();
}
行结束符
^^^^^^^^
提交内容只能包含以 LFUnix 风格)为行结束符的文件。
Windows 用户可以通过设置 ``core.autocrlf``,将 git 配置为在本地检出以 CRLFWindows 风格)为行结束符的文件,但提交时转换为以 LF 为行结束符。Github 中有一个 `文档 <https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings?platform=windows>`_ 介绍了如何配置此选项。
如果分支提交的信息在无意中带有 LF 行结束符,可以在 MSYS2 或 Unix 终端中运行以下命令,将其转换为 Unix 风格(请先切换到 IDF 工作目录并检查当前所在分支是否正确):
.. code-block:: bash
git rebase --exec 'git diff-tree --no-commit-id --name-only -r HEAD | xargs dos2unix && git commit -a --amend --no-edit --allow-empty' master
请注意,上述命令基于 master 进行 rebase若要基于其他分支进行 rebase请更改命令结尾的分支名称。
要更新单次提交中的文件行结束符,可以先运行 ``dos2unix FILENAME``,再运行 ``git commit --amend``
代码格式化
^^^^^^^^^^
ESP-IDF 使用 Astyle 来格式化源代码。配置存储在 :project_file:`tools/ci/astyle-rules.yml` 文件中。
所有组件都默认排除在格式化检查之外。可以将组件从 ``components_not_formatted_temporary`` 列表中删除,从而启用对该组件的格式化检查,然后运行:
.. code-block:: bash
pre-commit run --files <path_to_files> astyle_py
或者,也可以手动运行 ``astyle_py``。通过 ``pip install astyle_py==VERSION`` 安装该工具,确保安装的版本与 :project_file:`.pre-commit-config.yaml` 文件中指定的版本相同,然后运行:
.. code-block:: bash
astyle_py --rules=$IDF_PATH/tools/ci/astyle-rules.yml <path-to-file>
类型定义
^^^^^^^^
应使用 ``蛇形命名法`` (snake_case),并以 ``_t`` 后缀结尾:
.. code-block:: c
typedef int signed_32_bit_t;
枚举
^^^^
枚举应通过 `typedef` 定义,并使用命名空间:
.. code-block:: c
typedef enum
{
MODULE_FOO_ONE,
MODULE_FOO_TWO,
MODULE_FOO_THREE
} module_foo_t;
.. _assertions:
断言
^^^^
使用 ``assert.h`` 中定义的标准 C 函数 ``assert()`` 来检查源代码中应该为真的条件。在默认配置中,若断言条件返回 ``false````0``,则将调用 ``abort()`` 并触发 :doc:`严重错误 </api-guides/fatal-errors>`
``assert()`` 只用于检测那些无法修复的错误,这些错误因严重的内部逻辑漏洞或数据损坏而产生,导致程序无法继续运行。对于可修复的错误(如因无效外部输入而产生的错误),:doc:`应返回一个错误值 </api-guides/error-handling>`
.. note::
当断言一个类型为 ``esp_err_t`` 的值等于 ``ESP_OK`` 时,应使用 :ref:`esp-error-check-macro` 而不是 ``assert()``
可以将 ESP-IDF 项目配置为禁用断言(详见 :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL`)。因此,在 ``assert()`` 语句中调用的函数不应有副作用。
还需要使用特定技术来避免禁用断言时出现“变量定义但未使用”的警告,这种警告通常由以下代码模式引起:
.. code-block:: c
int res = do_something();
assert(res == 0);
一旦 ``assert`` 被优化掉,将不再使用 ``res`` 值,编译器会对此发出警告。但即使禁用了断言,仍必须调用 ``do_something()`` 函数。
当变量在单个语句中声明并初始化时,最好在新的一行上将其转换为 ``void``。编译器将不再发出警告,且变量仍可在最终的二进制文件中被优化掉:
.. code-block:: c
int res = do_something();
assert(res == 0);
(void)res;
如果变量是单独声明的,例如该变量被用于多个断言,则可以使用 GCC 属性 ``__attribute__((unused))`` 来声明它。编译器将不再发出任何有关未使用变量的警告,且变量仍可被优化掉:
.. code-block:: c
int res __attribute__((unused));
res = do_something();
assert(res == 0);
res = do_something_else();
assert(res != 0);
头文件保护
----------
所有面向公众的头文件应具有预处理器保护。推荐使用 pragma
.. code-block:: c
#pragma once
最好不要用以下模式:
.. code-block:: c
#ifndef FILE_NAME_H
#define FILE_NAME_H
...
#endif // FILE_NAME_H
除了保护宏,所有 C 语言头文件应具有 ``extern "C"`` 保护,使头文件能够在 C++ 代码中使用。头文件结构应遵循以下顺序:先是 ``pragma once``,再是 ``#include`` 语句,然后是 ``extern "C"`` 保护:
.. code-block:: c
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* declarations go here */
#ifdef __cplusplus
}
#endif
include 语句
------------
编写 ``#include`` 语句时,请尝试保持以下顺序:
* C 标准库头文件。
* 其他 POSIX 标准头文件及其常见扩展(如 ``sys/queue.h``)。
* 常见的 IDF 头文件(``esp_log.h````esp_system.h````esp_timer.h`` 以及 ``esp_sleep.h`` 等)。
* 其他组件的头文件,如 FreeRTOS。
* 当前组件的公共头文件。
* 私有头文件。
对于 C 标准库头文件和其他 POSIX 头文件,请使用尖括号 (``#include <stdio.h>``)。
对于所有其他头文件,请使用双引号 (``#include "esp_log.h"``)。
C++ 代码格式化
--------------
前文提到的 C 语言代码格式化规则同样适用于 C++ 代码格式化。如果这些规则不够,请遵循以下补充规则。
文件命名
^^^^^^^^
C++ 头文件的扩展名为 ``.hpp``。C++ 源文件的扩展名为 ``.cpp``。后者可帮助编译器将 C++ 源文件与 C 源文件区分开来。
命名
^^^^
* **类和结构体** 名称应使用首字母大写的 ``驼峰命名法`` (CamelCase)。成员变量和方法应使用 ``蛇形命名法`` (snake_case)。若 ``驼峰命名法`` 严重降低了可读性(例如命名 ``GPIOOutput`` 时),可允许使用下划线 ``_`` (例如,``GPIO_Output``),从而增强可读性。
* **命名空间** 应使用小写的 ``蛇形命名法``
* **模板** 应在函数声明的上一行进行指定。
* 面向对象编程 (OOP) 中的接口命名不应使用后缀 ``...Interface``。这种命名方式使得将来从普通类中提取接口或将接口转换为类时,更加容易实现而不会造成破坏性变更。
类成员顺序
^^^^^^^^^^
按照优先顺序:
* 先放置公共成员,然后是受保护成员,最后是私有成员。没有成员的公共、受保护或私有部分应省略。
* 先放置构造函数/析构函数,然后是成员函数,最后是成员变量。
例如:
.. code-block:: cpp
class ForExample {
public:
// 首先是构造函数,接着是默认构造函数,然后是析构函数
ForExample(double example_factor_arg);
ForExample();
~ForExample();
// 剩余的公共方法
set_example_factor(double example_factor_arg);
// 公共成员变量
uint32_t public_data_member;
private:
// 首先是私有方法
void internal_method();
// 然后是私有成员变量
double example_factor;
};
空格
^^^^
* 在命名空间内部不要缩进。
* 将 ``public````protected````private`` 标签的缩进级别与相应 ``class`` 标签的缩进级别保持一致。
简单示例
^^^^^^^^
.. code-block:: cpp
// spaceship.h 文件
#ifndef SPACESHIP_H_
#define SPACESHIP_H_
#include <cstdlib>
namespace spaceships {
class SpaceShip {
public:
SpaceShip(size_t crew);
size_t get_crew_size() const;
private:
const size_t crew;
};
class SpaceShuttle : public SpaceShip {
public:
SpaceShuttle();
};
class Sojuz : public SpaceShip {
public:
Sojuz();
};
template <typename T>
class CargoShip {
public:
CargoShip(const T &cargo);
private:
T cargo;
};
} // 命名空间 spaceships
#endif // SPACESHIP_H_
// spaceship.cpp 文件
#include "spaceship.h"
namespace spaceships {
// 如果仅在初始化列表中初始化值
// 则可以将大括号放置在同一行
SpaceShip::SpaceShip(size_t crew) : crew(crew) { }
size_t SpaceShip::get_crew_size() const
{
return crew;
}
SpaceShuttle::SpaceShuttle() : SpaceShip(7)
{
// 进一步初始化
}
Sojuz::Sojuz() : SpaceShip(3)
{
// 进一步初始化
}
template <typename T>
CargoShip<T>::CargoShip(const T &cargo) : cargo(cargo) { }
} // 命名空间 spaceships
CMake 代码风格
--------------
- 使用四个空格缩进。
- 每行最多包含 120 个字符。当分割行时,尽量注重可读性(例如,在单独的行上进行关键字匹配或参数匹配)。
- 在 ``endforeach()````endif()`` 等可选括号中不要添加任何内容。
- 使用小写 (``with_underscores``) 来命名指令、函数和宏。
- 对于局部变量,使用小写 (``with_underscores``)。
- 对于全局变量,使用大写 (``WITH_UNDERSCORES``)。
- 其他方面遵循 cmake-lint_ 项目的默认设置。
使用 EditorConfig 配置项目代码风格
----------------------------------
EditorConfig 可以帮助开发者在不同的编辑器和集成开发环境 (IDE) 之间定义和维护一致的代码风格。EditorConfig 项目包含定义代码风格的文件格式以及一些文本编辑器插件可以帮助编辑器读取文件格式并遵循定义风格。EditorConfig 文件易于阅读,并且与版本控制系统配合良好。
详情请浏览 `EditorConfig <https://editorconfig.org>`_ 网站。
第三方组件代码风格
------------------
ESP-IDF 集成了许多第三方组件,这些组件可能具有不同的代码风格。
FreeRTOS
^^^^^^^^
有关FreeRTOS 采用的代码风格,请参阅 `FreeRTOS 风格指南 <https://www.freertos.org/zh-cn-cmn-s/FreeRTOS-Coding-Standard-and-Style-Guide.html>`_。FreeRTOS 源代码的格式化是通过 `Uncrustify <https://github.com/uncrustify/uncrustify>`_ 自动完成的,因此 ESP-IDF FreeRTOS 组件中存有 FreeRTOS 代码风格的 Uncrustify 配置 (``uncrustify.cfg``) 副本 。
如果修改了 FreeRTOS 源文件,可以通过以下步骤重新格式化更新后的文件:
1. 确保系统安装了 Uncrustify (v0.69.0)。
2. 在更新后的 FreeRTOS 源文件上运行以下命令(其中 ``source.c`` 是需要格式化的源文件的路径)。
.. code-block:: bash
uncrustify -c $IDF_PATH/components/freertos/FreeRTOS-Kernel/uncrustify.cfg --replace source.c --no-backup
代码文档
--------
请参阅此处的指南::doc:`documenting-code`
结构体
------
待编写。
语言特性
--------
待编写。
.. _cmake-lint: https://github.com/richq/cmake-lint