本文件說明編寫 C 程式庫的經驗法則和規則 Fuchsia SDK 中發布的版本。
會為 C++ 程式庫編寫不同的文件。雖然 C++ 是 對 C 的延伸,而且對本文件、 編寫 C++ 程式庫的模式與 C 截然不同
本文件大部分都與 介面上。這不是完整的 C 格式指南,且 幾乎不會說明 C 來源檔案的內容也不是 說明文件評分量表 (不過公開介面 )。
部分 C 程式庫的外部限制與這些限制相牴觸 不過,編寫這類演算法並不容易 因為我們無法寫出所有可能的規則舉例來說,C 標準程式庫本身不會遵循 這些規則本文仍須遵守本文件,
目標
ABI 穩定性
部分配備穩定 ABI 的 Fuchsia 介面將以 C 形式發布 程式庫這份文件的目標之一是協助 Fuchsia 輕鬆上手 編寫及維護穩定的 ABI因此,我們 最好不要使用 C 語言提供的某些功能 可能會產生令人意外或複雜影響 存取 API我們也禁止非標準編譯器擴充功能 無法假設第三方使用任何特定編譯器 以下列舉幾個 DDK 的例外狀況。
資源管理
本文件的組成部分說明資源管理的最佳做法 。包括資源、Zircon 控點和任何其他類型的 資源。
標準化
我們也希望對 Fuchsia C 採用合理一致的標準 程式庫特別是命名配置。Out 參數 排序也是標準化的另一個例子
FFI 適用性
外部函式介面 (FFI) 會耗費一些心力 瀏覽。許多非 C 語言支援 C 介面。 這些 FFI 系統的複雜程度差異不大, 升級至精密的 libclang 工具。有一些 我們在製定這些決策時考量了 FFI 相容性的考量因素。
語言版本
C
Fuchsia C 程式庫是根據 C11 標準編寫 (使用 支援多種例外狀況 (例如 Unix 信號支援) 尤其與我們的 C 程式庫 ABI 密切相關)。C99 法規遵循並不是 目標。
特別是 Fuchsia C 程式碼可以使用 <threads.h>
和
C11 標準程式庫的 <stdatomic.h>
標頭,以及
_Thread_local
和對齊語言功能。
執行緒本機應該使用 thread_local
拼寫
<threads.h>
,而非內建的 _Thread_local
。同樣地
偏好來自<stdalign.h>
的alignas
和alignof
,而非
_Alignas
和_Alignof
。
請注意,編譯器支援的標記可能會變更
再也不是件繁重乏味的工作舉例來說,GCC 的 -m96bit-long-double
旗標會修改
長雙精度浮點數的大小我們假定您並沒有使用這類標記。
最後,我們的
IDK 結合了外部定義的介面和 Fuchsia 專屬
。在這些情況下,我們允許某些暴力內容。舉例來說:
libc 定義了 thrd_get_zx_handle
等函式,以及
dlopen_vmo
。這些名稱並非完全遵循
規則: 程式庫的名稱不是前置字串。這麼做
比較容易將這些名稱與其他函式搭配使用,例如
thrd_current
和 dlopen
,因此允許例外狀況。
C++
雖然 C++ 不是 C 確切的超集,但我們仍會設計 C 程式庫
可以從 C++ 存取Fuchsia C 標頭應與
C++11、C++14 和 C++17 標準。尤其是函式
宣告必須為 extern "C"
,如下所述。
請勿在單一標頭中混用 C 和 C++ 介面。
建立獨立的 cpp
子目錄,並將 C++ 介面放置在其位置
自己的標頭
程式庫版面配置和命名
Fuchsia C 程式庫有名稱。這個名稱會決定其中包含路徑 (如程式庫命名文件所述) 和 ID 程式庫中的各個選項
在本文件中,程式庫一律會命名為 tag
,且在各種情況下
稱為 tag
、TAG
、Tag
或 kTag
,代表
特殊的詞典慣例tag
必須是單一 ID
不要加上底線代碼的格式為全小寫的
規則運算式 [a-z][a-z0-9]*
。只要使用較短的標記取代標記
程式庫名稱版本,例如 zx
而不是 zircon
。
標頭 foo.h
的 include 路徑,如程式庫所述
命名文件應為 lib/tag/foo.h
。
標題版面配置
C 程式庫中的單一標頭包含幾種內容。
- 著作權橫幅
- 標頭防護
- 檔案納入項目清單
- 外部 C 防衛
- 常數宣告
- 外部符號宣告
- 包含外部函式宣告
- 靜態內嵌函式
- 巨集定義
防潮
在標頭中使用 #ifndef guard。如下所示:
#ifndef SOMETHING_MUMBLE_H_
#define SOMETHING_MUMBLE_H_
// code
// code
// code
#endif // SOMETHING_MUMBLE_H_
該定義的確切形式如下:
- 以標準 include 路徑指向標頭
- 將所有的「.」、「/」和「-」取代為「_」
- 將所有字母轉換為大寫
- 加上結尾的 _
例如,位於 SDK lib/tag/object_bits.h
中的標頭
應包含標頭保護 LIB_TAG_OBJECT_BITS_H_
。
包容
並在標題中加入實際使用的內容。尤其是任何公開標頭 包含在來源檔案之中。
程式庫可以依附 C 標準程式庫標頭。
有些程式庫也可能依附一部分 POSIX 標頭。等於 是否適合待後續的 libc API 審查。
常數定義
程式庫中的大部分常數都是編譯時間常數,
透過 #define
。您還可以使用唯讀變數,透過宣告
extern const TYPE NAME;
,因為有時如果需要
表示常數 (尤其是某些形式的 FFI)。這個區段
說明如何在標頭中提供編譯時間常數。
編譯時間常數有多種類型。
- 單一整數常數
- 列舉整數常數
- 浮點常數
單一整數常數
單一整數常數在程式庫 TAG
中有一些 NAME
,
定義如下所示
#define TAG_NAME EXPR
其中 EXPR
為下列任一格式 (uint32_t
)
((uint32_t)23)
((uint32_t)0x23)
((uint32_t)(EXPR | EXPR | ...))
列舉整數常數
已知程式庫中有一組名為 NAME
的列舉常數
TAG
,相關編譯時間常數組合包含下列部分。
首先, typedef 用於提供類型名稱、大小和
簽署。typedef 必須是明確大小的整數
類型。例如,如果您使用 uint32_t
:
typedef uint32_t tag_name_t;
每個常數都會有以下形式:
#define TAG_NAME_... EXPR
其中 EXPR
是少數編譯時間整數的類型之一
常數 (一律以括號括住):
((tag_name_t)23)
((tag_name_t)0x23)
((tag_name_t)(TAG_NAME_FOO | TAG_NAME_BAR | ...))
請勿加入值的數量 (難以維護) 常數集會不斷成長
浮點常數
浮點常數與單一整數常數類似,
只是使用其他機制來描述型別浮點值
常數結尾必須是 f
或 F
;雙常數沒有後置字串;
長雙常數的結尾必須是 l
或 L
。十六進位版本
可以使用浮點常數。
// A float constant
#define TAG_FREQUENCY_LOW 1.0f
// A double constant
#define TAG_FREQUENCY_MEDIUM 2.0
// A long double constant
#define TAG_FREQUENCY_HIGH 4.0L
函式宣告
函式宣告的名稱皆應以 tag_...
開頭。
函式宣告應置於 extern "C"
守衛中。這些
正透過 __BEGIN_CDECLS
和
來自 compiler.h 的 __END_CDECLS
巨集。
函式參數
您必須為函式參數命名,例如:
// Disallowed: missing parameter name
zx_status_t tag_frob_vmo(zx_handle_t, size_t num_bytes);
// Allowed: all parameters named
zx_status_t tag_frob_vmo(zx_handle_t vmo, size_t num_bytes);
因此應清楚說明要使用哪些參數,以及哪些參數 借用。避免使用用戶端可能擁有的 呼叫函式之後的資源如果無法這麼做,請考慮 函式名稱或其 參數。例如:
zx_status_t tag_frobinate_subtle(zx_handle_t foo);
zx_status_t tag_frobinate_if_frobable(zx_handle_t foo);
zx_status_t tag_try_frobinate(zx_handle_t foo);
zx_status_t tag_frobinate(zx_handle_t maybe_consumed_foo);
按照慣例,參數會在函式簽章中最後存在,且
名稱應為 out_*
。
血管函式
應避免為如 printf 以外的所有項目避免使用 Variadic 函式
函式。這些函式應記錄自己的格式字串
與 compiler.h 的 __PRINTFLIKE
屬性簽訂合約。
靜態內嵌函式
允許使用靜態內嵌函式,且最好
類似函式的巨集僅限內嵌 (也不是 static
) C
函式有複雜的連結規則和極少用例。
類型
建議明確大小的整數類型 (例如 int32_t
)
不含明確大小的類型 (例如 int
或 unsigned long int
)。一個
參照 POSIX 檔案描述元時,int
將不受限制。
以及 C 或 POSIX 標頭中的 size_t
類型定義。
在可能的情況下,介面中提及的指標類型應參照
特定類型的物件這包括不透明結構的指標。void*
是
可接受,用來參照原始記憶體,以及如果介面為
不透明的使用者 Cookie 或背景資料
不透明/煽情露骨類型
定義不透明結構時,建議使用 void*
。不透明
結構體應宣告如下:
typedef struct tag_thing tag_thing_t;
公開的結構應宣告如下:
typedef struct tag_thing {
} tag_thing_t;
保留欄位
結構中的所有保留欄位都應記錄為用途 。
本文件日後將提供指引 說明 C 介面中的字串參數。
匿名類型
不允許使用頂層匿名類型。匿名建築 聯集可以在其他結構內,或在函式內使用 主體,因為之後不屬於頂層命名空間。適用對象 例項,以下範例包含允許的匿名聯集。
typedef struct tag_message {
tag_message_type_t type;
union {
message_foo_t foo;
message_bar_t bar;
};
} tag_message_t;
函式 typedefs
允許使用函式類型的 Typedef。
使用 zx_status_t
的函式不應超載傳回值
成功和正成功值函式不應超載
傳回值的 zx_status_t
,其中含有不包含的其他值
如 zircon/errors.h 中所述。
狀態退貨
偏好使用 zx_status_t
做為傳回值,以說明與下列項目相關的錯誤:
Zircon 原始物件和 I/O。
資源管理
程式庫可在多種資源中投放流量。記憶體與 Zircon 是許多通用資源的例子 程式庫程式庫也可以定義自己的資源 需要管理的生命週期
所有資源的擁有權應明確定義。轉乘
資源應在函式名稱中明確表示例如:
create
和 take
負責轉移擁有權的函式。
程式庫應保持緊密的記憶體。下列函式分配的記憶體
tag_thing_create
應透過 tag_thing_destroy
或部分
而非透過 free
程式庫不應公開全域變數。而是改為提供 運用函式操控該狀態具有 process-global 的程式庫 狀態必須是動態連結,而非靜態連結。常見模式是 可將程式庫分割為無狀態的靜態部分 以及含有全域狀態的小型動態程式庫
特別是 errno
介面,其為全域執行緒本機
「全域」) 應避免使用新的程式碼。
連結
程式庫中的預設符號顯示設定應設為隱藏。使用 匯出符號的許可清單,或明確顯示 要匯出的符號註解。
C 程式庫不得匯出 C++ 符號。
演化
淘汰
已淘汰的函式應標示 __DEPRECATED 屬性 來自 compiler.h。觀眾也應該要加上說明註解 建議改成做什麼,以及如何追蹤淘汰情形。
不允許或令人反感的用語功能
本節說明的語言功能不得或不應 用在介面中的 C 語言程式碼 然後阻止它們
列舉
C 列舉已遭禁止。他們從 ABI 的角度來看而有條不紊。
- 用來代表列舉類型常數的整數大小為 會影響編譯器 (和編譯器標記)。
- 列舉的帶正性相當脆弱,因為將負值 列舉會變更基礎類型。
比特田
C 的 Bitfields 遭禁止。它們從 ABI 的角度來看而易用。 有許多不直覺的銳利邊緣
請注意,這適用於 C 語言功能,不適用於 會顯示位元旗標C 位元功能如下所示:
typedef struct tag_some_flags {
// Four bits for the frob state.
uint8_t frob : 4;
// Two bits for the grob state.
uint8_t grob : 2;
} tag_some_flags_t;
我們建議以編譯時間整數的形式公開位元標記 常數。
空白參數清單
C 允許函式 with_empty_parameter_lists()
與 functions_that_take(void)
不同。第一個意思是「以
第二種則是「從零開始」
參數。由於空白參數清單有危險,我們會予以禁止。
彈性陣列成員
這個 C99 功能可讓您宣告不完整的陣列, 結構中最後一個成員,其中包含多個參數。例如:
typedef struct foo_buffer {
size_t length;
void* elements[];
} foo_buffer_t;
不過,如果是 DDK 結構,則在下列情況中,可以使用這個模式: 參照符合此 Header-plus-payload 的外部版面配置 。
宣告 0 大小陣列成員的 GCC 同樣是
模組對應
這些是 Clang 擴充功能的一部分,旨在嘗試以類似 C 語言的方式 其中許多與標頭導向編譯有關的問題紫紅色時 我們很有可能未來會投資這類工具 我們目前不支援這些格式
編譯器擴充功能
從定義上來說,無法跨工具鍊遷移。
這尤其包括封裝的屬性或碎片, 因為 DDK 的例外狀況
DDK 結構通常反映了不相符的外部版面配置 執行 ABI 測試舉例來說,它可能會參照 較不合乎語言規定可以透過以下程式碼形式表示: 例如 pragma pack