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 个空格缩进,完全不使用制表符。

  • 每行不应超过 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!
    

    do

    if (yes) {
        // Success!
    
  • 当所有函数和全局变量在当前编译单元之外不需要时,应该将它们声明为 static。

  • 在头文件中声明外部函数和变量。

命名约定#

  • 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。