前言
最近博主在學(xué)習(xí) RT-Thread 這個(gè)開(kāi)源項(xiàng)目,開(kāi)始慢慢學(xué)習(xí)和理解它的開(kāi)源代碼,慢慢開(kāi)始接觸了它的代碼規(guī)范。 我個(gè)人認(rèn)為,參與一個(gè)開(kāi)源項(xiàng)目的第一步,就是要好好理解它的規(guī)范,其中代碼編寫規(guī)范就是很重要的一環(huán)。
RT-Thread 編程風(fēng)格
這是一份 RT-Thread 開(kāi)發(fā)人員的開(kāi)發(fā)指引。RT-Thread 做為一份開(kāi)源軟件,它需要由不同的人采用合作的方式完成,這份文檔是開(kāi)發(fā)人員的一個(gè)指引。RT-Thread 的開(kāi)發(fā)人員請(qǐng)遵守這樣的編程風(fēng)格。同時(shí)對(duì)于使用 RT-Thread 的用戶,也可通過(guò)這份文檔了解 RT-Thread代碼內(nèi)部一些約定從而比較容易的把握到 RT-Thread 的實(shí)現(xiàn)方式。
1.目錄名稱
目錄名稱如果無(wú)特殊的需求,請(qǐng)使用全小寫的形式;目錄名稱應(yīng)能夠反映部分的意思,例如各芯片移植由其芯片名稱構(gòu)成或芯片類別構(gòu)成;components 目錄下能夠反映組件的意義。
2.文件名稱
文件名稱如果無(wú)特殊的需求(如果是引用其他地方,可以保留相應(yīng)的名稱),請(qǐng)使用全小寫的形式。另外為了避免文件名重名的問(wèn)題,一些地方請(qǐng)盡量不要使用通用化、使用頻率高的名稱。
設(shè)備驅(qū)動(dòng)源碼文件:drv_class.c?的命名方式,如:
drv_spi.c
drv_gpio.c
3.頭文件定義
C 語(yǔ)言頭文件為了避免多次重復(fù)包含,需要定義一個(gè)符號(hào)。這個(gè)符號(hào)的定義形式請(qǐng)采用如下的風(fēng)格:
?
1????#ifndef?__FILE_H__ 2????#define?__FILE_H__ 3????/*?header?file?content?*/ 4????#endif
?
即定義的符號(hào)兩側(cè)采用 “__” 以避免重名,另外也可以根據(jù)文件名中是否包含多個(gè)詞語(yǔ)而采用 “_” 連接起來(lái)。
4.文件頭注釋
在每個(gè)源文件文件頭上,應(yīng)該包括相應(yīng)的版權(quán)信息,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.結(jié)構(gòu)體定義
結(jié)構(gòu)體名稱請(qǐng)使用小寫英文名的形式,單詞與單詞之間采用 “_” 連接,例如:
?
1????struct?rt_list_node 2????{ 3????????struct?rt_list_node?*next; 4????????struct?rt_list_node?*prev; 5????};
?
其中,"{","}" 獨(dú)立占用一行,后面的成員定義使用縮進(jìn)的方式定義。
結(jié)構(gòu)體等的類型定義請(qǐng)以結(jié)構(gòu)體名稱加上 “_t” 的形式作為名稱,例如:
?
1????typedef?struct?rt_list_node?rt_list_t;
?
因?yàn)閮?nèi)核中對(duì)象引用方便的緣故,采用了對(duì)象內(nèi)核指針作為類型定義的形式,例如:
?
1????typedef?struct?rt_timer*?rt_timer_t;
?
6.宏定義
在RT-Thread中,請(qǐng)使用大寫英文名稱作為宏定義,單詞之間使用 “_” 連接,例如:
?
1????#define?RT_TRUE?????????????????????????1
?
7.函數(shù)名稱、聲明
函數(shù)名稱請(qǐng)使用小寫英文的形式,單詞之間使用 “_” 連接。提供給上層應(yīng)用使用的 API接口,必須在相應(yīng)的頭文件中聲明;如果函數(shù)入口參數(shù)是空,必須使用 void 作為入口參數(shù),例如:
?
1rt_thread_t?rt_thread_self(void);
?
內(nèi)部靜態(tài)函數(shù)命名:以下劃線開(kāi)頭,使用?_class_method?格式,不攜帶_rt_開(kāi)頭,如內(nèi)核或驅(qū)動(dòng)文件中的函數(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()????????????????????
?
調(diào)用注冊(cè)設(shè)備接口的函數(shù)命名:使用?rt_hw_class_init()?格式,舉例:
?
1int?rt_hw_uart_init(void) 2int?rt_hw_spi_init(void)
?
8.注釋編寫
請(qǐng)使用英文做為注釋,使用中文注釋將意味著在編寫代碼時(shí)需要來(lái)回不停的切換中英文輸入法從而打斷編寫代碼的思路。并且使用英文注釋也能夠比較好的與中國(guó)以外的技術(shù)者進(jìn)行交流。
語(yǔ)句注釋:
源代碼的注釋不應(yīng)該過(guò)多,更多的說(shuō)明應(yīng)該是代碼做了什么,僅當(dāng)個(gè)別關(guān)鍵點(diǎn)才需要一些相應(yīng)提示性的注釋以解釋一段復(fù)雜的算法它是如何工作的。對(duì)語(yǔ)句的注釋只能寫在它的上方或右方,其他位置都是非法的。
?
1/*?你的英文注釋?*/
?
函數(shù)注釋:
注釋以?/**?開(kāi)頭,以?*/?結(jié)尾,中間寫入函數(shù)注釋,組成元素如下,每個(gè)元素描述之間空一行,且首列對(duì)齊:
@brief + 簡(jiǎn)述函數(shù)作用。在描述中,著重說(shuō)明該函數(shù)的作用,每句話首字母大寫,句尾加英文句號(hào)。
@note + 函數(shù)說(shuō)明。在上述簡(jiǎn)述中未能體現(xiàn)到的函數(shù)功能或作用的一些點(diǎn),可以做解釋說(shuō)明,每句話首字母大寫,句尾加英文句號(hào)。
@see + 相關(guān) API 羅列。若有與當(dāng)前函數(shù)相關(guān)度較高的 API,可以進(jìn)行列舉。
@param + 以參數(shù)為主語(yǔ) + be 動(dòng)詞 + 描述,說(shuō)明參數(shù)的意義或來(lái)源。
@return + 枚舉返回值 + 返回值的意思,若返回值為數(shù)據(jù),則直接介紹數(shù)據(jù)的功能。
@warning + 函數(shù)使用注意要點(diǎn)。在函數(shù)使用時(shí),描述需要注意的事項(xiàng),如使用環(huán)境、使用方式等。每句話首字母大寫,句尾加英文句號(hào)。
注釋模版請(qǐng)參見(jiàn):rt-thread/src/ipc.c 源碼文件,英文注釋請(qǐng)參考使用 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.縮進(jìn)及分行
縮進(jìn)請(qǐng)采用 4 個(gè)空格的方式。如果沒(méi)有什么特殊意義,請(qǐng)?jiān)?“{” 后進(jìn)行分行,并在下一行都采用縮進(jìn)的方式,例如:
?
1????if?(condition) 2????{ 3????????/*?others?*/ 4????}
?
唯一的例外是 switch 語(yǔ)句,switch-case 語(yǔ)句采用 case 語(yǔ)句與 switch 對(duì)齊的方式,例如:
?
1????switch?(value) 2????{ 3????case?value1: 4????????break; 5????}
?
case 語(yǔ)句與前面的 switch 語(yǔ)句對(duì)齊,后續(xù)的語(yǔ)句則采用縮進(jìn)的方式。分行上,如果沒(méi)有什么特殊考慮,請(qǐng)不要在代碼中連續(xù)使用兩個(gè)以上的空行。
10.大括號(hào)與空格
從代碼閱讀角度,建議每個(gè)大括號(hào)單獨(dú)占用一行,而不是跟在語(yǔ)句的后面,例如:
?
1????if?(condition) 2????{ 3????????/*?others?*/ 4????}
?
匹配的大括號(hào)單獨(dú)占用一行,代碼閱讀起來(lái)就會(huì)有相應(yīng)的層次而不會(huì)容易出現(xiàn)混淆的情況。空格建議在非函數(shù)方式的括號(hào)調(diào)用前留一個(gè)空格以和前面的進(jìn)行區(qū)分,例如:
?
1????if?(x?<=?y) 2????{ 3????????/*?others?*/ 4????} 5 6????for?(index?=?0;?index??
建議在括號(hào)前留出一個(gè)空格(涉及的包括 if、for、while、switch 語(yǔ)句),而運(yùn)算表達(dá)式中,運(yùn)算符與字符串間留一個(gè)空格。另外,不要在括號(hào)的表達(dá)式兩側(cè)留空格,例如:
?
1????if?(?x?<=?y?) 2????{ 3????????/*?other?*/ 4????}?
這樣括號(hào)內(nèi)兩側(cè)的空格是不允許的。
11.trace、log信息
在 RT-Thread 中,普遍使用的 log 方式是 rt_kprintf。rt_kprintf 在 RT-Thread 被實(shí)現(xiàn)成一個(gè)采用輪詢、非中斷方式的字串輸出,能夠適合于在中斷這類"即時(shí)"顯示日志的場(chǎng)合。因?yàn)檫@種輪詢方式的存在,也必然會(huì)影響到日志輸出的時(shí)序關(guān)系。
建議在代碼中不要頻繁的使用 rt_kprintf 作為日志輸出,除非你真正的明白,你的代碼運(yùn)行占用的時(shí)間多一些也沒(méi)什么關(guān)系。
日志輸出應(yīng)該被設(shè)計(jì)成正常情況下是關(guān)閉狀態(tài)(例如通過(guò)一個(gè)變量或宏就能夠開(kāi)啟),并且當(dāng)真正輸出日志時(shí),日志是易懂易定位問(wèn)題的方式。"天書式"的日志系統(tǒng)是糟糕的,不合理的。
12.函數(shù)
在內(nèi)核編程中,函數(shù)應(yīng)該盡量精簡(jiǎn),僅完成相對(duì)獨(dú)立的簡(jiǎn)單功能。函數(shù)的實(shí)現(xiàn)不應(yīng)該太長(zhǎng),函數(shù)實(shí)現(xiàn)太長(zhǎng),應(yīng)該反思能夠如何修改(或拆分)使得函數(shù)更為精簡(jiǎn)、易懂。
13.對(duì)象
RT-Thread 內(nèi)核采用了 C 語(yǔ)言對(duì)象化技術(shù),命名表現(xiàn)形式是:對(duì)象名結(jié)構(gòu)體表示類定義、對(duì)象名 + 動(dòng)詞短語(yǔ)形式表示類方法,例如:
?
1????struct?rt_timer 2????{ 3????????struct?rt_object?parent; 4????????/*?other?fields?*/ 5????}; 6????typedef?struct?rt_timer*?rt_timer_t;?
結(jié)構(gòu)體定義 rt_timer 代表了 timer 對(duì)象的類定義;
?
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 + 動(dòng)詞短語(yǔ)的形式表示能夠應(yīng)用于 timer 對(duì)象的方法。
在創(chuàng)建一個(gè)新的對(duì)象時(shí),應(yīng)該思考好,對(duì)象的內(nèi)存操作處理:是否允許一個(gè)靜態(tài)對(duì)象存在,或僅僅支持從堆中動(dòng)態(tài)分配的對(duì)象。
14.格式化代碼
格式化代碼是指通過(guò)腳本自動(dòng)整理你的代碼,并使其符合 RT-Thread 的編碼規(guī)范。本文提供以下兩種自動(dòng)格式化代碼方法,可以自行選擇或配合使用。
使用 astyle 格式化
用 astyle 自動(dòng)格式化代碼,參數(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ù)空格、縮進(jìn)、函數(shù)語(yǔ)句等的規(guī)范。
使用 formatting 格式化
使用 formatting 掃描文件來(lái)格式化代碼:formatting 可以滿足編碼規(guī)則的基本要求,如:
將源文件編碼統(tǒng)一為 UTF-8
將 TAB 鍵替換為 4 空格
將每行末尾多余的空格刪除,并統(tǒng)一換行符為 ‘ \n’
審核編輯:湯梓紅
評(píng)論