作者:架構師李肯
前言
最近博主在學習 RT-Thread 這個開源項目,開始慢慢學習和理解它的開源代碼,慢慢開始接觸了它的代碼規(guī)范。
我個人認為,參與一個開源項目的第一步,就是要好好理解它的規(guī)范,其中代碼編寫規(guī)范就是很重要的一環(huán)。
RT-Thread 編程風格
這是一份 RT-Thread 開發(fā)人員的開發(fā)指引。RT-Thread 做為一份開源軟件,它需要由不同的人采用合作的方式完成,這份文檔是開發(fā)人員的一個指引。RT-Thread 的開發(fā)人員請遵守這樣的編程風格。同時對于使用 RT-Thread 的用戶,也可通過這份文檔了解 RT-Thread代碼內部一些約定從而比較容易的把握到 RT-Thread 的實現(xiàn)方式。
1.目錄名稱
目錄名稱如果無特殊的需求,請使用全小寫的形式;目錄名稱應能夠反映部分的意思,例如各芯片移植由其芯片名稱構成或芯片類別構成;components 目錄下能夠反映組件的意義。
2.文件名稱
文件名稱如果無特殊的需求(如果是引用其他地方,可以保留相應的名稱),請使用全小寫的形式。另外為了避免文件名重名的問題,一些地方請盡量不要使用通用化、使用頻率高的名稱。
設備驅動源碼文件:drv_class.c
的命名方式,如:
- drv_spi.c
- drv_gpio.c
3.頭文件定義
C 語言頭文件為了避免多次重復包含,需要定義一個符號。這個符號的定義形式請采用如下的風格:
1 #ifndef __FILE_H__
2 #define __FILE_H__
3 /* header file content */
4 #endif
即定義的符號兩側采用 “__” 以避免重名,另外也可以根據(jù)文件名中是否包含多個詞語而采用 “_” 連接起來。
4.文件頭注釋
在每個源文件文件頭上,應該包括相應的版權信息,Change Log 記錄:
1/*
2 * Copyright (c) 2006-2020, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2006-03-18 Bernard the first version
9 * 2006-04-26 Bernard add semaphore APIs
10 */
例如采用如上的形式。
5.結構體定義
結構體名稱請使用小寫英文名的形式,單詞與單詞之間采用 “_” 連接,例如:
1 struct rt_list_node
2 {
3 struct rt_list_node *next;
4 struct rt_list_node *prev;
5 };
其中,"{","}" 獨立占用一行,后面的成員定義使用縮進的方式定義。
結構體等的類型定義請以結構體名稱加上 “_t” 的形式作為名稱,例如:
1 typedef struct rt_list_node rt_list_t;
因為內核中對象引用方便的緣故,采用了對象內核指針作為類型定義的形式,例如:
1 typedef struct rt_timer* rt_timer_t;
6.宏定義
在RT-Thread中,請使用大寫英文名稱作為宏定義,單詞之間使用 “_” 連接,例如:
1 #define RT_TRUE 1
7.函數(shù)名稱、聲明
函數(shù)名稱請使用小寫英文的形式,單詞之間使用 “_” 連接。提供給上層應用使用的 API接口,必須在相應的頭文件中聲明;如果函數(shù)入口參數(shù)是空,必須使用 void 作為入口參數(shù),例如:
1rt_thread_t rt_thread_self(void);
內部靜態(tài)函數(shù)命名:以下劃線開頭,使用 _class_method
格式,不攜帶_rt_
開頭,如內核或驅動文件中的函數(shù)命名:
1/* IPC object init */
2static rt_err_t _ipc_object_init()
3
4/* UART driver ops */
5static rt_err_t _uart_configure()
6static rt_err_t _uart_control()
調用注冊設備接口的函數(shù)命名:使用 rt_hw_class_init()
格式,舉例:
1int rt_hw_uart_init(void)
2int rt_hw_spi_init(void)
8.注釋編寫
請使用英文做為注釋,使用中文注釋將意味著在編寫代碼時需要來回不停的切換中英文輸入法從而打斷編寫代碼的思路。并且使用英文注釋也能夠比較好的與中國以外的技術者進行交流。
語句注釋:
源代碼的注釋不應該過多,更多的說明應該是代碼做了什么,僅當個別關鍵點才需要一些相應提示性的注釋以解釋一段復雜的算法它是如何工作的。對語句的注釋只能寫在它的上方或右方,其他位置都是非法的。
1/* 你的英文注釋 */
函數(shù)注釋:
注釋以 /**
開頭,以 */
結尾,中間寫入函數(shù)注釋,組成元素如下,每個元素描述之間空一行,且首列對齊:
- @brief + 簡述函數(shù)作用。在描述中,著重說明該函數(shù)的作用,每句話首字母大寫,句尾加英文句號。
- @note + 函數(shù)說明。在上述簡述中未能體現(xiàn)到的函數(shù)功能或作用的一些點,可以做解釋說明,每句話首字母大寫,句尾加英文句號。
- @see + 相關 API 羅列。若有與當前函數(shù)相關度較高的 API,可以進行列舉。
- @param + 以參數(shù)為主語 + be 動詞 + 描述,說明參數(shù)的意義或來源。
- @return + 枚舉返回值 + 返回值的意思,若返回值為數(shù)據(jù),則直接介紹數(shù)據(jù)的功能。
- @warning + 函數(shù)使用注意要點。在函數(shù)使用時,描述需要注意的事項,如使用環(huán)境、使用方式等。每句話首字母大寫,句尾加英文句號。
注釋模版請參見:rt-thread/src/ipc.c 源碼文件,英文注釋請參考使用 grammarly 以及谷歌翻譯。
1/**
2 * @brief The function will initialize a static event object.
3 *
4 * @note For the static event object, its memory space is allocated by the compiler during compiling,
5 * and shall placed on the read-write data segment or on the uninitialized data segment.
6 * By contrast, the rt_event_create() function will allocate memory space automatically
7 * and initialize the event.
8 *
9 * @see rt_event_create()
10 *
11 * @param event is a pointer to the event to initialize. It is assumed that storage for the event
12 * will be allocated in your application.
13 *
14 * @param name is a pointer to the name that given to the event.
15 *
16 * @param value is the initial value for the event.
17 * If want to share resources, you should initialize the value as the number of available resources.
18 * If want to signal the occurrence of an event, you should initialize the value as 0.
19 *
20 * @param flag is the event flag, which determines the queuing way of how multiple threads wait
21 * when the event is not available.
22 * The event flag can be ONE of the following values:
23 *
24 * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority.
25 *
26 * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method
27 * (also known as first-come-first-served (FCFS) scheduling strategy).
28 *
29 * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
30 * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
31 * the first-in-first-out principle, and you clearly understand that all threads involved in
32 * this event will become non-real-time threads.
33 *
34 * @return Return the operation status. When the return value is RT_EOK, the initialization is successful.
35 * If the return value is any other values, it represents the initialization failed.
36 *
37 * @warning This function can ONLY be called from threads.
38 */
39rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)
40{
41 ...
42}
9.縮進及分行
縮進請采用 4 個空格的方式。如果沒有什么特殊意義,請在 “{” 后進行分行,并在下一行都采用縮進的方式,例如:
1 if (condition)
2 {
3 /* others */
4 }
唯一的例外是 switch 語句,switch-case 語句采用 case 語句與 switch 對齊的方式,例如:
1 switch (value)
2 {
3 case value1:
4 break;
5 }
case 語句與前面的 switch 語句對齊,后續(xù)的語句則采用縮進的方式。分行上,如果沒有什么特殊考慮,請不要在代碼中連續(xù)使用兩個以上的空行。
10.大括號與空格
從代碼閱讀角度,建議每個大括號單獨占用一行,而不是跟在語句的后面,例如:
1 if (condition)
2 {
3 /* others */
4 }
匹配的大括號單獨占用一行,代碼閱讀起來就會有相應的層次而不會容易出現(xiàn)混淆的情況。空格建議在非函數(shù)方式的括號調用前留一個空格以和前面的進行區(qū)分,例如:
1 if (x <= y)
2 {
3 /* others */
4 }
5
6 for (index = 0; index < MAX_NUMBER; index ++)
7 {
8 /* others */
9 }
建議在括號前留出一個空格(涉及的包括 if、for、while、switch 語句),而運算表達式中,運算符與字符串間留一個空格。另外,不要在括號的表達式兩側留空格,例如:
1 if ( x <= y )
2 {
3 /* other */
4 }
這樣括號內兩側的空格是不允許的。
11.trace、log信息
在 RT-Thread 中,普遍使用的 log 方式是 rt_kprintf。rt_kprintf 在 RT-Thread 被實現(xiàn)成一個采用輪詢、非中斷方式的字串輸出,能夠適合于在中斷這類"即時"顯示日志的場合。因為這種輪詢方式的存在,也必然會影響到日志輸出的時序關系。
建議在代碼中不要頻繁的使用 rt_kprintf 作為日志輸出,除非你真正的明白,你的代碼運行占用的時間多一些也沒什么關系。
日志輸出應該被設計成正常情況下是關閉狀態(tài)(例如通過一個變量或宏就能夠開啟),并且當真正輸出日志時,日志是易懂易定位問題的方式。"天書式"的日志系統(tǒng)是糟糕的,不合理的。
12.函數(shù)
在內核編程中,函數(shù)應該盡量精簡,僅完成相對獨立的簡單功能。函數(shù)的實現(xiàn)不應該太長,函數(shù)實現(xiàn)太長,應該反思能夠如何修改(或拆分)使得函數(shù)更為精簡、易懂。
13.對象
RT-Thread 內核采用了 C 語言對象化技術,命名表現(xiàn)形式是:對象名結構體表示類定義、對象名 + 動詞短語形式表示類方法,例如:
1 struct rt_timer
2 {
3 struct rt_object parent;
4 /* other fields */
5 };
6 typedef struct rt_timer* rt_timer_t;
結構體定義 rt_timer 代表了 timer 對象的類定義;
1rt_timer_t rt_timer_create(const char* name,
2 void (*timeout)(void* parameter),
3 void* parameter,
4 rt_tick_t time, rt_uint8_t flag);
5rt_err_t rt_timer_delete(rt_timer_t timer);
6rt_err_t rt_timer_start(rt_timer_t timer);
7rt_err_t rt_timer_stop(rt_timer_t timer);
rt_timer + 動詞短語的形式表示能夠應用于 timer 對象的方法。
在創(chuàng)建一個新的對象時,應該思考好,對象的內存操作處理:是否允許一個靜態(tài)對象存在,或僅僅支持從堆中動態(tài)分配的對象。
14.格式化代碼
格式化代碼是指通過腳本自動整理你的代碼,并使其符合 RT-Thread 的編碼規(guī)范。本文提供以下兩種自動格式化代碼方法,可以自行選擇或配合使用。
使用 astyle 格式化
用 astyle 自動格式化代碼,參數(shù)如下:
1 --style=allman
2 --indent=spaces=4
3 --indent-preproc-block
4 --pad-oper
5 --pad-header
6 --unpad-paren
7 --suffix=none
8 --align-pointer=name
9 --lineend=linux
10 --convert-tabs
11 --verbose
能滿足函數(shù)空格、縮進、函數(shù)語句等的規(guī)范。
使用 formatting 格式化
使用 formatting 掃描文件來格式化代碼:formatting 可以滿足編碼規(guī)則的基本要求,如:
- 將源文件編碼統(tǒng)一為 UTF-8
- 將 TAB 鍵替換為 4 空格
- 將每行末尾多余的空格刪除,并統(tǒng)一換行符為 ‘
’
-
開源軟件
+關注
關注
0文章
210瀏覽量
15900 -
C語言
+關注
關注
180文章
7604瀏覽量
136683 -
編程
+關注
關注
88文章
3614瀏覽量
93685 -
編碼
+關注
關注
6文章
940瀏覽量
54813 -
RT-Thread
+關注
關注
31文章
1285瀏覽量
40079
發(fā)布評論請先 登錄
相關推薦
評論