你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

编码指导原则

CNTK编码样式

本页记录CNTK源代码中使用的约定。 编写新代码时,请遵循这些约定。 遵循常识并分解超出合理限制的函数, (几个屏幕页面) ,使用有意义的名称,注释良好,并保持批注和代码同步等。

基础知识:缩进、间距和大括号

代码始终使用四个空格缩进。 不允许在代码中的任何位置使用制表符。 唯一的例外是 Makefiles、其他生成系统或数据文件,其中制表符在语法上是必需的。

以下代码块缩进:

  • 控制语句的主体:for、if、while、switch 等。
  • Free 语句块,即不遵循任何控制语句的左大括号和右大括号。 这些对象有时用于限制对象的生存期。
  • 类和函数的主体。
  • 语句从上一行继续。
  • case 语句中的代码在 case 语句后面开始,并缩进。

以下事项未缩进:

  • 命名空间的内容。
  • Case 标签
  • 访问控制说明符。

具有长参数列表的函数声明可以拆分为多行。 拆分行上的参数声明应缩进到函数声明的左括号。 对具有长参数列表的函数的调用可能会拆分为多个行,拆分行应缩进到关联的函数语句的左括号。

代码是使用 Allman 或 BSD Unix 样式大括号编写的。 此样式将与控件语句关联的大括号置于下一行,缩进到与控件语句相同的级别。 大括号中的语句缩进到下一级别,建议不要省略大括号,即使对于小块也是如此。

空格位于以下位置:

  • 围绕所有二进制运算符,包括赋值
  • 在关键字和括号之间
  • 标识符或关键字与大括号之间
  • 在不结束行的逗号和分号之后

以下位置不存在空格:

  • 分号和逗号之前
  • 括号内侧
  • 函数名称和其参数列表之间
  • 在一元运算符及其操作数之间
  • 在空参数列表中
  • 在标签和冒号之间
  • 围绕范围运算符 ::

应在单独的行上编写包含多个类的成员初始值设定项列表和基类列表。 这使得很容易发现错误。

namespace Microsoft { namespace MSR { namespace CNTK {

Matrix ImplodeSelf(int x);
int ConfuseUs(float y);

class MainPart:
    public Head,
    protected Heart
{
public:
    MainPart():
        m_weight(99),
        m_height(180)
    {}
private:
    void EatMore();
    int m_consume, m_repeater;
};

template <typename Box>
void Inspect(Box & container)
{
    switch (container)
    {
    case 1:
        PrepareIt();
        break;

    case 2:
        Finish();
        break;

    default:
        break;
    }

    for (int i = 0; i < 30; ++i)
    {
        container << EatMore();
    }
    return container;
}

} } }

命名约定

  • 类和命名空间名称使用 UpperCamelCase aka PascalCase
  • 所有上限 (SQL、CNTK、...) 中通常拼写的名称可以保留在所有大写中。
  • 全局和公共静态函数、堆栈变量和类成员 (类变量) 使用 lowerCamelCase。
  • 类成员函数 (方法) 使用 UpperCamelCase。
  • 宏和常量使用UPPER_SNAKE_CASE。
  • 类型为 UpperCamelCase 的模板参数。
  • 不允许使用类型前缀、匈牙利表示法等。 如果需要消除歧义,请使用有意义的后缀,例如 floatMatrix 和 normalizedDoubleMatrix。

名称前缀

  • m_ 成员变量
  • s_ 用于任何上下文中的静态变量
  • g_ 对于全局变量,应尽可能多地 (避免)

变量名称应为名词。 函数名称应为谓词,但 getter 除外,可以是名词。 例如,名为 position 的类属性将具有 setter SetPosition () 和 getter Position () 。

文件名约定

C++ 文件应具有 .cpp 扩展名,而头文件应具有 .h 扩展名。 不允许使用空格和下划线。 不建议在文件名中使用数字。

#define GOOD_MACRO(x) x
void CallOut();
unsigned const g_theAnswer = 42;

class SolveAllProblems 
{
public:
    void DoWhatWeNeed();
    static void SetBugsOff();
    int m_countReasons;
protected:
    void NeverWorking();
    static void GetReason();
    int internalCounter;
private:
    void InternalNeeds();
    static void ShowReason();
    int m_countShows;
};

template <typename TypeParam, int numberOfReasons>
void CallGlobal(boost::array<TypeParam, numberOfReasons> const &array);

预处理器

强烈建议不要使用预处理器进行条件编译,因为它会导致代码腐烂。 仅当它不可避免时使用,例如,使用可选依赖项时。 特殊情况是使用条件编译来基于允许的平台排除整个文件。

对于条件编译的特定于平台的代码,应旨在编写可移植代码,无论平台如何,都可以使用相同的代码。 使用 Boost 库可在这方面有所帮助。 如果必须根据平台使用不同的代码,请尝试将其封装在帮助程序函数中,以便平台之间的代码量保持在最低水平。