NEP 45 — C 語言風格指南#

作者:

Charles Harris <charlesr.harris@gmail.com>

狀態:

有效

類型:

流程

建立日期:

2012-02-26

決議:

numpy/numpy#11911

摘要#

本文檔提供了組成 NumPy C 實作的 C 程式碼的編碼慣例。

動機與範疇#

NumPy C 編碼慣例基於 Guido van Rossum 的 Python PEP 7 – C 程式碼風格指南,並新增了一些嚴格規定。

由於 NumPy 慣例與 PEP 7 中的慣例非常接近,因此 PEP 被用作範本,NumPy 的新增內容和變更會放在適當的位置。

用法與影響#

C 程式碼編碼慣例有很多種,必須強調的是,NumPy 慣例的主要目標不是選擇「最佳」慣例(對此肯定會有歧見),而是達成一致性。

違反特定規則的兩個充分理由

  1. 當應用該規則會使程式碼降低可讀性時,即使對於習慣閱讀遵循規則的程式碼的人來說也是如此。

  2. 為了與也違反該規則的周圍程式碼保持一致(可能是由於歷史原因)– 儘管這也是清理別人爛攤子的機會。

向後相容性#

無影響。

詳細描述#

C 語言變體#

  • 使用 C99(即 ISO/IEC 9899:1999 定義的標準)。

  • 不要使用 GCC 擴充功能(例如,不要撰寫沒有尾隨反斜線的多行字串)。最好將長字串分成多行,如下所示

    "blah blah"
    "blah blah"
    

    這適用於 MSVC,否則 MSVC 會在非常長的字串上崩潰。

  • 所有函數宣告和定義都必須使用完整原型(即,指定所有引數的類型)。

  • 主要編譯器(gcc、VC++、少數其他編譯器)沒有編譯器警告。

注意

NumPy 仍然產生需要解決的編譯器警告。

程式碼排版#

  • 使用 4 個空格縮排,完全不使用 Tab 字元。

  • 任何行都不應超過 80 個字元。如果此規則和前一個規則加在一起沒有給您足夠的程式碼空間,則表示您的程式碼太過複雜 – 請考慮使用子程式。

  • 任何行都不應以空白字元結尾。如果您認為您需要大量的尾隨空白字元,請重新考慮;別人的編輯器可能會將其作為例行程序刪除。

  • 函數定義樣式:函數名稱在第 1 欄,最外層的大括號在第 1 欄,區域變數宣告後空一行

    static int
    extra_ivars(PyTypeObject *type, PyTypeObject *base)
    {
        int t_size = PyType_BASICSIZE(type);
        int b_size = PyType_BASICSIZE(base);
    
        assert(t_size >= b_size); /* type smaller than base! */
        ...
        return 1;
    }
    

    如果過渡到 C++,則此形式可能會放寬,以便預定內聯的簡短類別方法可以將傳回類型與函數名稱放在同一行。但是,這仍有待確定。

  • 程式碼結構:關鍵字(如 iffor)與後面的左括號之間有一個空格;括號內沒有空格;所有 if 分支都用大括號括起來,並且 if 的同一行沒有陳述式。它們應格式化為如下所示

    if (mro != NULL) {
        one_line_statement;
    }
    else {
        ...
    }
    
    
    for (i = 0; i < n; i++) {
        one_line_statement;
    }
    
    
    while (isstuff) {
        dostuff;
    }
    
    
    do {
        stuff;
    } while (isstuff);
    
    
    switch (kind) {
        /* Boolean kind */
        case 'b':
            return 0;
        /* Unsigned int kind */
        case 'u':
            ...
        /* Anything else */
        default:
            return 3;
    }
    
  • return 陳述式不應取得多餘的括號

    return Py_None; /* correct */
    return(Py_None); /* incorrect */
    
  • 函數和巨集呼叫樣式:foo(a, b, c),左括號前沒有空格,括號內沒有空格,逗號前沒有空格,每個逗號後有一個空格。

  • 始終在賦值、布林和比較運算子周圍加上空格。在使用大量運算子的運算式中,在外層(最低優先順序)運算子周圍加上空格。

  • 斷行長行:如果可以,請在最外層引數清單中的逗號後斷行。始終適當地縮排連續行

    PyErr_SetString(PyExc_TypeError,
            "Oh dear, you messed up.");
    

    此處的適當表示至少雙重縮排(8 個空格)。不必將所有內容都與函數呼叫的左括號對齊。

  • 當您在二元運算子處斷行長運算式時,運算子會放在前一行的末尾,例如

    if (type > tp_dictoffset != 0 &&
            base > tp_dictoffset == 0 &&
            type > tp_dictoffset == b_size &&
            (size_t)t_size == b_size + sizeof(PyObject *)) {
        return 0;
    }
    

    請注意,多行布林運算式中的項已縮排,以便程式碼區塊的開頭清晰可見。

  • 在函數、結構定義和函數內的主要區段周圍放置空行。

  • 註解放在它們描述的程式碼之前。多行註解應如下所示

    /*
     * This would be a long
     * explanatory comment.
     */
    

    應謹慎使用尾隨註解。而不是

    if (yes) { // Success!
    

    執行

    if (yes) {
        // Success!
    
  • 所有函數和全域變數在當前編譯單元外部不需要時,都應宣告為靜態。

  • 在標頭檔中宣告外部函數和變數。

命名慣例#

  • NumPy 公共函數沒有一致的前綴,但它們都以某種前綴開頭,後跟底線,並且採用駝峰式命名法:PyArray_DescrAlignConverterNpyIter_GetIterNext。未來,名稱應採用 Npy*_PublicFunction 的形式,其中星號是適當的內容。

  • 公共巨集應具有 NPY_ 前綴,然後使用大寫,例如 NPY_DOUBLE

  • 私有函數應為小寫,並帶有底線,例如:array_real_get。不應使用單個前導底線,但由於歷史意外,目前某些函數名稱違反了該規則。

注意

名稱以單個底線開頭的函數應在某個時候重新命名。

函數文件#

NumPy 目前沒有 C 函數文件標準,但需要一個標準。大多數 NumPy 函數在程式碼中沒有文件記錄,這應該改變。一種可能性是 Doxygen,並帶有一個外掛程式,以便用於 Python 函數的相同 NumPy 樣式也可以用於記錄 C 函數,請參閱 doc/cdoc/ 中的檔案。

討論#

numpy/numpy#11911 建議將最初為 doc/C_STYLE_GUIDE.rst 的提案轉變為 NEP。