libcudf C++ 文档指南

这些指南适用于使用 Doxygen 风格格式记录所有 libcudf C++ 源文件,尽管实际上只有公共 API 和类被发布

版权许可

此处包含版权注释,但也可在编码指南文档中提及。以下是应出现在每个 C++ 源文件开头的许可头注释。

/*
 * Copyright (c) 2021-2022, NVIDIA CORPORATION.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://apache.ac.cn/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

注释应以 /* 开头,而不是 /**,这样 Doxygen 就不会处理它。

此外,以下是关于版权年份的规则。

  • 新建文件应包含创建年份
  • 修改过的文件应包含创建年份和修改年份 (例如 2019-2021)

如果内容没有改变(例如仅格式化),则无需更改版权年份。

Doxygen

Doxygen 工具用于从源代码中的 C++ 注释生成 HTML 页面。Doxygen 识别并解析块注释,并在遇到Doxygen 命令时执行专门的输出格式化。

Doxygen 在注释块中可识别近 200 个命令(本文档中也称为标签)。本文档提供了关于在 libcudf C++ 源代码中使用哪些命令/标签以及如何使用它们的指南。

Doxygen 过程可以通过Doxyfile中的选项进行定制。

以下是一些 libcudf 的 Doxyfile 中的自定义选项。

选项设置描述
PROJECT_NAMElibcudf主页上使用的标题
PROJECT_NUMBER22.02.00 版本号
EXTENSION_MAPPINGcu=C++ cuh=C++cucuh 文件视为 C++ 文件处理
INPUTmain_page.md regex.md unicode.md ../include要处理的嵌入式 Markdown 文件和源代码目录
FILE_PATTERNS*.cpp *.hpp *.h *.c *.cu *.cuh要处理的文件扩展名

块注释

对于描述函数、类和其他类型、组以及文件的块注释,请使用以下风格。

/**
 * description text and
 * doxygen tags go here
 */

Doxygen 注释块仅以 /** 开头并以 */ 结尾,这两行上没有其他内容。不要在 Doxygen 块的第一行和最后一行添加破折号 ----- 或额外的星号 *****。注释块必须紧贴在它所引用的源代码行之前。为了垂直对齐所描述的项目,可以适当地缩进注释块。请参见下面的示例部分。

/***/ 之间的注释块中的每一行都应该以一个空格后跟一个星号开头。这些行上的任何文本,包括标签声明,都应该在星号后的一个空格后开始。

标签/命令名称

使用 @ 作为 Doxygen 命令的前缀(例如 @brief, @code 等)

Markdown

Doxygen 工具支持在注释块中使用有限的 Markdown 格式,包括链接、表格、列表等。在某些情况下,需要在源文本文件的可读性与 Doxygen 格式化网页的可读性之间进行权衡。例如,在 Markdown 表格中使用 '' 字符和管道符 '|' 会有一些可读性限制。

避免使用直接的 HTML 标签。尽管 Doxygen 支持 Markdown,且 Markdown 支持 HTML 标签,但 Doxygen 对 Markdown 中 HTML 的支持也是有限的。

示例

以下示例涵盖了在 libcudf 中用于文档化 C++ 代码的大多数 Doxygen 块注释和标签风格。

/**
 * @file source_file.cpp
 * @brief Description of source file contents
 *
 * Longer description of the source file contents.
 */

/**
 * @brief One sentence description of the class.
 *
 * @ingroup optional_predefined_group_id
 *
 * Longer, more detailed description of the class.
 *
 * @tparam T Short description of each template parameter
 * @tparam U Short description of each template parameter
 */
template <typename T, typename U>
class example_class {

  void get_my_int();            ///< Simple members can be documented like this
  void set_my_int( int value ); ///< Try to use descriptive member names

  /**
   * @brief Short, one sentence description of the member function.
   *
   * A more detailed description of what this function does and what
   * its logic does.
   *
   * @code
   * example_class<int> inst;
   * inst.set_my_int(5);
   * int output = inst.complicated_function(1,dptr,fptr);
   * @endcode
   *
   * @param[in]     first  This parameter is an input parameter to the function
   * @param[in,out] second This parameter is used both as an input and output
   * @param[out]    third  This parameter is an output of the function
   *
   * @return The result of the complex function
   */
  T complicated_function(int first, double* second, float* third)
  {
      // Do not use doxygen-style block comments
      // for code logic documentation.
  }

 private:
  int my_int;                ///< An example private member variable
};

/**
 * @brief Short, one sentence description of this free function.
 *
 * @ingroup optional_predefined_group_id
 *
 * A detailed description must start after a blank line.
 *
 * @code
 * template<typename T>
 * struct myfunctor {
 *   bool operator()(T input) { return input % 2 > 0; }
 * };
 * free_function<myfunctor,int>(myfunctor{},12);
 * @endcode
 *
 * @throw cudf::logic_error if `input_argument` is negative or zero
 *
 * @tparam functor_type The type of the functor
 * @tparam input_type The datatype of the input argument
 *
 * @param[in] functor        The functor to be called on the input argument
 * @param[in] input_argument The input argument passed into the functor
 * @return The result of calling the functor on the input argument
 */
template <class functor_type, typename input_type>
bool free_function(functor_type functor, input_type input_argument)
{
  CUDF_EXPECTS( input_argument > 0, "input_argument must be positive");
  return functor(input_argument);
}

/**
 * @brief Short, one sentence description.
 *
 * @ingroup optional_predefined_group_id
 *
 * Optional, longer description.
 */
enum class example_enum {
  first_enum,   ///< Description of the first enum
  second_enum,  ///< Description of the second enum
  third_enum    ///< Description of the third enum
};

描述

注释描述应清楚详细地说明如何根据任何输入创建输出。包括任何性能和边界考虑因素。还要包括对参数值的任何限制,以及是否声明了任何默认值。不要忘记说明如何处理或生成空值。此外,如果可能,尽量包含一个简短的示例

@brief

@brief 文本应是一个简短的单句描述。Doxygen 在输出页面中显示此文本的空间不大。@brief 行后面始终应有一个空白注释行。

更长的描述是注释文本中未被任何 Doxygen 命令标记的其余部分。

/**
 * @brief Short description.
 *
 * Long description.
 *

@copydoc

头文件中的声明文档应清晰完整。您可以使用@copydoc标签来避免为函数定义重复注释块。

  /**
   * @copydoc complicated_function(int,double*,float*)
   *
   * Any extra documentation.
   */

此外,当文档化仅因 stream 参数而不同的 detail 函数时,@copydoc 也很有用。

/**
 * @copydoc cudf::segmented_count_set_bits(bitmask_type const*,std::vector<size_type> const&)
 *
 * @param[in] stream Optional CUDA stream on which to execute kernels
 */
std::vector<size_type> segmented_count_set_bits(bitmask_type const* bitmask,
                                                std::vector<size_type> const& indices,
                                                rmm::cuda_stream_view stream = cudf::get_default_stream());

注意,您必须指定函数的完整签名,包括可选参数,以便 Doxygen 能够找到它。

函数参数

以下标签应按此处指定的顺序出现在函数注释块的末尾附近

命令描述
@throw 指定函数可能抛出异常的条件
@tparam 每个模板参数的描述
@param 每个函数参数的描述
@return 返回的对象或值的简短描述

@throw

对于函数可能抛出的每个异常,在 Doxygen 块中添加一个@throw注释行。您只需要包含函数本身抛出的异常。如果函数调用了可能抛出异常的其他函数,则无需在此处记录这些异常。

包含异常名称,不带反引号,以便 Doxygen 可以正确添加引用链接。

 *
 * @throw cudf::logic_error if `input_argument` is negative or zero
 *

使用 @throws 也是可以接受的,但 VS Code 和其他工具只对 @throw 进行语法高亮。

@tparam

为函数声明的每个模板参数添加一个@tparam注释行。在 Doxygen 标签后指定的参数名称必须与模板参数名称完全匹配。

 *
 * @tparam functor_type The type of the functor
 * @tparam input_type The datatype of the input argument
 *

定义应详细说明参数的要求。例如,如果模板用于仿函数(functor)或谓词(predicate),则描述预期的输入类型和输出。

@param

为传递给此函数的每个函数参数添加一个@param注释行。在 Doxygen 标签后指定的参数名称必须与函数的参数名称匹配。如果从声明和参数名称本身不清楚方向,还应在 @param 后附加 [in][out][in,out]

 *
 * @param[in]     first  This parameter is an input parameter to the function
 * @param[in,out] second This parameter is used both as an input and output
 * @param[out]    third  This parameter is an output of the function
 *

如果可能,建议垂直对齐这 3 列文本,以便在源代码编辑器中更容易阅读。

@return

如果函数返回对象或值,请在注释块末尾添加一个单独的@return注释行。包括对返回内容的简要描述。

/**
 * ...
 *
 * @return A new column of type INT32 and no nulls
 */

不要在 @return 注释中包含返回对象的类型。

内联示例

在文档化函数或其他声明时,在注释块中包含源代码示例通常很有帮助。使用@code@endcode对来包含内联示例。

Doxygen 支持 C++ 和其他几种编程语言(例如 Python、Java)的语法高亮。默认情况下,@code 标签根据其所在的源代码使用语法高亮。

 *
 * @code
 * auto result = cudf::make_column( );
 * @endcode
 *

您可以通过在标签中指定文件扩展名来指定不同的语言

 *
 * @code{.py}
 * import cudf
 * s = cudf.Series([1,2,3])
 * @endcode
 *

如果您希望在示例中使用伪代码,请使用以下格式

 *
 * Sometimes pseudocode is clearer.
 * @code{.pseudo}
 * s = int column of [ 1, 2, null, 4 ]
 * r = fill( s, [1, 2], 0 )
 * r is now [ 1, 0, 0, 4 ]
 * @endcode
 *

编写示例片段时,使用完全限定的类名可以让 Doxygen 为示例添加引用链接。

 *
 * @code
 * auto result1 = make_column( ); // reference link will not be created
 * auto result2 = cudf::make_column( ); // reference link will be created
 * @endcode
 *

虽然使用三个反引号 ``` 来表示示例块也可以工作,但它们在 VS Code 和其他源代码编辑器中不够突出。

不要在声明的注释中使用@example标签,否则 Doxygen 会将整个源文件解释为示例源代码。然后,该源文件将作为单独的“示例”页面发布在输出中。

弃用

对于将在未来版本中移除的 API,在注释块中添加一个单独的@deprecated注释行。在弃用注释中提及替代/替换的 API。

/**
 * ...
 *
 * @deprecated This function is deprecated. Use another new function instead.
 */

命名空间

Doxygen 输出包含一个“命名空间”页面,显示处理的文件中所有带有注释块声明的命名空间。以下是一个用于命名空间声明的 Doxygen 描述注释示例。

/**
 * @brief cuDF interfaces
 *
 * This is the top-level namespace which contains all cuDF functions and types.
 */
namespace CUDF_EXPORT cudf {

每个唯一的命名空间声明只应包含一次描述注释。否则,如果找到多个描述,Doxygen 将在输出页面中以任意顺序聚合这些描述。

如果引入新的命名空间,仅为一个声明提供一个描述块,而不是每个出现的地方都提供。

组/模块

将声明分组到模块中有助于用户在 Doxygen 页面中找到 API。通常,常用函数已经按逻辑分组到头文件中,但 Doxygen 不会在输出中自动以这种方式分组。Doxygen 输出包含一个“模块”页面,该页面使用分组 Doxygen 命令将项目组织到组中。这些命令可以将头文件、源文件甚至命名空间中的常用函数进行分组。组还可以通过在现有组中定义新组来嵌套。

对于 libcudf,所有组层级都在doxygen_groups.h头文件中定义。doxygen_groups.h文件不需要包含在任何其他源文件中,因为此文件中的定义仅由 Doxygen 工具用于在“模块”页面中生成组。仅修改此文件以添加或更新组。现有组已仔细构建和命名,因此新组应经过深思熟虑后添加。

创建新的 API 时,使用@ingroup标签和doxygen_groups.h文件中的组引用 ID 来指定其所属组。

namespace CUDF_EXPORT cudf {

/**
 * @brief ...
 *
 * @ingroup transformation_fill
 *
 * @param ...
 * @return ...
 */
std::unique_ptr<column> fill(table_view const& input,...);

}  // namespace cudf

您还可以使用带有 @{ ... @} 对的 @addtogroup 来自动将 Doxygen 注释块包含到组中。

namespace CUDF_EXPORT cudf {
/**
 * @addtogroup transformation_fill
 * @{
 */

/**
 * @brief ...
 *
 * @param ...
 * @return ...
 */
std::unique_ptr<column> fill(table_view const& input,...);

/** @} */
}  // namespace cudf

这样做只是省去了在文件内的每个 Doxygen 注释块中添加 @ingroup 的麻烦。请确保在 @addtogroup 命令块之后包含一个空白行,以便 Doxygen 知道它不适用于源代码中紧随其后的内容。请注意,如果带有 @{ ... @} 对的 @addtogroup 包含了命名空间声明,Doxygen 将不会为其中的项分配组。因此,如上例所示,将 @addtogroup 和 @{ ... @} 放在命名空间声明的大括号之间。

组标签摘要

标签/命令使用位置
@defgroup 仅用于doxygen_groups.h,应包含组的标题。
@ingroup 在头文件中的声明语句的单个 Doxygen 块注释内部使用。
@addtogroup 在同一个文件中的命名空间声明内,用于代替 @ingroup 来处理多个声明。不要指定组标题。
@{ ... @} 仅与 @addtogroup 一起使用。

构建 Doxygen 输出

我们建议使用 conda (conda install doxygen) 或 Linux 包管理器 (sudo apt install doxygen) 安装 Doxygen。或者,您可以从源代码构建和安装 Doxygen

要构建 libcudf HTML 文档,只需在包含 Doxyfilecpp/doxygen 目录中运行 doxygen 命令。libcudf 文档也可以在 cmake 构建目录(例如 cpp/build)中使用 cmake --build . --target docs_cudf 命令构建。Doxygen 读取并处理 cpp/include/ 目录下的所有相关源文件。输出将生成在 cpp/doxygen/html/ 目录中。您可以在任何网络浏览器中加载该目录中生成的本地 index.html 文件来查看结果。

要在远程服务器上查看构建好的文档,您可以使用 Python 运行一个简单的 HTTP 服务器:cd html && python -m http.server。然后在本地网络浏览器中打开 <IP address>:8000,将 <IP address> 替换为您运行 HTTP 服务器的机器的 IP 地址。

Doxygen 输出仅用于构建公共 API 和类的文档。例如,输出不应包含 detail/src 文件的文档,这些目录已在 Doxyfile 配置中排除。通过构建/CI 系统发布时,Doxygen 输出将出现在我们的外部RAPIDS 网站上。