Press "Enter" to skip to content

Doxygen – 治好了我的代码注释强迫症

在编写大量代码时,或者与多人协作进行开发时,文字记录很重要(文档或注释),尤其对于开发者本人回顾之前写过的代码和 code reviewer 查看。目前,代码中的注释进是程序员们最经常使用的记录方法。但是,由于每个人的注释风格不同,有些开发者甚至对API并不进行任何注释描述,导致很多影响生产效率的问题,也增加了软件维护的成本。很多人对于怎样写注释也会困惑和反感。因此,使用一些代码文档工可以帮助这个开发流程高效、规范地进行。

使用这种工具我个人认为最大的帮助是可以节省时间,并提高生产力

  • 不用去构思如何去写注释,用哪一种风格去写,从而专注于构思代码。
  • 减少看其他人代码的时间成本。

Doxygen 就是这样一种编写软件参考文档的工具。该文档是直接以注释的形式写在代码中的,因此比较容易修改和查看。而且对于代码里的注释,Doxygen还可以为C++项目生成html,Latex和PDF的文档。

虽然使用过Doxygen的开发者可能并不会经常阅读由Doxygen生成的那些文档,而是更加倾向于阅读源代码里的注释。但是并不妨碍Doxygen成为一款很受欢迎而且高效的代码注释工具,至少我个人很喜欢Doxygen对于注释的种种规范。

关于如何使用Dxygen的适用方法,可以参照官方的教程,不会有比这个更加权威并且详细的说明了:https://www.doxygen.nl/manual/index.html

在之前分享过的C/C++ Coding Style(https://zhuanlan.zhihu.com/p/267645803)中,我介绍了使用Doxygen进行代码注释的一些风格,在这里详细介绍一些我经常使用的Doxygen属性。

一个例子

例如一个头文件中:

/*****************************************************************************
 * Portfolio Info
 ****************************************************************************/

/**
 * @file doxygen.h
 * @brief File containing example of doxygen usage for quick reference.
 *
 * Here typically goes a more extensive explanation of what the header
 * defines. Doxygens tags are words preceeded by either a backslash @\\
 * or by an at symbol @@.
 *
 * @author Chris Wu
 * @date 24 Oct 2019
 * @see <http://www.doxygen.nl/manual/index.html>
 */

/** @addtogroup DOXYGEN_API
 * @brief Doxygen api example.
 *
 * Detailed api description.
 *
 * @{
 */

#ifndef _TEMP_DOXYGEN_H
#define _TEMP_DOXYGEN_H

#ifdef __cplusplus
extern "C" {
#endif

/* INCLUDE FILES */
#include <stdint.h>
//#include <system_header2.h>

//#include "local_header1.h"
//#include "local_header2.h"

/* GLOBAL DEFINES */
/**
 * @brief Use brief, otherwise the index won't have a brief explanation.
 *
 * Detailed explanation.
 */
typedef enum eEnumMode
{
    ENUM_FIRST,  /**< Some documentation for first. */
    BOXENUM_SECOND, /**< Some documentation for second. */
    BOXENUM_ETC     /**< Etc. */
} tEnumMode;

/**
 * @brief Use brief, otherwise the index won't have a brief explanation.
 *
 * Detailed explanation.
 */
typedef struct BoxStruct
{
    int a;    /**< Some documentation for the member BoxStruct#a. */
    int b;    /**< Some documentation for the member BoxStruct#b. */
    double c; /**< Etc. */
} tBoxStruct;

/* GLOBAL VARIABLES */
extern int giValue;

/* GLOBAL FUNCTIONS */
/**
 * @brief Example showing how to document a function with Doxygen.
 *
 * Description of what the function does. This part may refer to the parameters
 * of the function, like @p param1 or @p param2. A word of code can also be
 * inserted like @c this which is equivalent to <tt>this</tt> and can be useful
 * to say that the function returns a @c void or an @c int. If you want to have
 * more than one word in typewriter font, then just use @<tt@>.
 * We can also include text verbatim,
 * when the language is not the one used in the current source file (but
 * <b>be careful</b> as this may be supported only by recent versions
 * of Doxygen). By the way, <b>this is how you write bold text</b> or,
 * if it is just one word, then you can just do @b this.
 *
 * @param [in] param1 Description of the first parameter of the function.
 * @param [out] param2 The second one, which follows @p param1, and represents output.
 *
 * @return Describe what the function returns.
 * @retval XXX_OK if successful.
 *
 * @see doxygen_theSecondFunction
 * @see Box_The_Last_One
 * @see <http://website/>
 * @note Something to note.
 * @warning Warning.
 */
int doxygen_theFirstFunction(int param1, int param2);

/**
 * @brief A simple stub function to show how links do work.
 *
 * Links are generated automatically for webpages (like <http://www.google.com>)
 * and for structures, like sBoxStruct. For typedef-ed types use
 * #tBoxStruct.
 * For functions, automatic links are generated when the parenthesis () follow
 * the name of the function, like doxygen_theFirstFunction().
 * Alternatively, you can use #doxygen_theFirstFunction.
 * @return @c NULL is always returned.
 */
void doxygen_theSecondFunction(void);

#ifdef __cplusplus
}
#endif

#endif /* _TEMP_DOXYGEN_H */
/** @}*/

版权声明注释

/*****************************************************************************
 * Portfolio Info
 ****************************************************************************/

我会把项目的版权声明信息放在这。

文件描述注释

/**
 * @file header.h
 * @brief Brief file introduction.
 *
 * Detailed file introduction.
 *
 * @author Name
 * @date day month year
 * @see Related link.
 */

在这里列出这个文件的名字,和一些基本信息:

  • @file: 文件名
  • @brief:文件一句话介绍
  • @author:文件作者
  • @date:修改日期
  • @see:额外的一些参考信息,比如有用过的链接

API Group

/** @addtogroup DOXYGEN_API
 * @brief Doxygen api example.
 *
 * Detailed api description.
 *
 * @{
 */
  • API codes...
/** @}*/

这两段代码搭配使用,一前一后,中间包含API代码。这部分定义了这个文件的API group,可以在Doxygen生成的文件中查看到其中包含的所有API group(比如变量,类,函数)信息。

变量前的注释

/**
 * @brief Use brief, otherwise the index won't have a brief explanation.
 *
 * Detailed explanation.
 */
typedef struct BoxStruct
{
    int a;    /**< Some documentation for the member BoxStruct#a. */
    int b;    /**< Some documentation for the member BoxStruct#b. */
    double c; /**< Etc. */
} tBoxStruct;

对于一些需要说明的变量,可以在变量前加上一段Doxygen注释,方便度代码的人查看,也可以声称在比如HTML等文档中。

API 函数注释

/* GLOBAL FUNCTIONS */
/**
 * @brief Example showing how to document a function with Doxygen.
 *
 * Description of what the function does. This part may refer to the parameters
 * of the function, like @p param1 or @p param2. A word of code can also be
 * inserted like @c this which is equivalent to <tt>this</tt> and can be useful
 * to say that the function returns a @c void or an @c int. If you want to have
 * more than one word in typewriter font, then just use @<tt@>.
 * We can also include text verbatim,
 * when the language is not the one used in the current source file (but
 * <b>be careful</b> as this may be supported only by recent versions
 * of Doxygen). By the way, <b>this is how you write bold text</b> or,
 * if it is just one word, then you can just do @b this.
 *
 * @param [in] param1 Description of the first parameter of the function.
 * @param [out] param2 The second one, which follows @p param1, and represents output.
 *
 * @return Describe what the function returns.
 * @retval XXX_OK if successful.
 *
 * @see doxygen_theSecondFunction
 * @see Box_The_Last_One
 * @see <http://website/>
 * @note Something to note.
 * @warning Warning.
 */
int doxygen_theFirstFunction(int param1, int param2);

对于所有头文件的函数,都需要进行函数注释。

  • @param:标记变量 [in] [out]表示输入输出方向
  • @return:返回值描述
  • @retval:具体返回值及其含义
  • @see:link信息
  • @note:备注信息
  • @warning:需要函数使用者注意的信息,比如:功能未经完全验证

总结

我对自己的要求是,对于 public 头文件中的所有信息,都应该进行详细的注释,方便使用者查看。而在代码源文件中可以不详细注释,自己可以理解。但是也需要按照Doxygen的格式列出基本信息方便回顾代码的时候提醒自己,让自己了解自己当时在想些什么,为什么要这么做。

对于一个开发团队,尤其是大公司,都会有严格规定的标准,包括代码风格,各种git hook,还有类似Doxygen这种文档工具。有些工程师会觉得这些“标准”很繁琐,很难要求自己遵守。但是不可否认的是,各种代码标准的目的不是以折磨程序员为乐趣,而是为了提高程序员的生产力,提高代码的可读性,可维护性,可靠性。

但是有时候,为了追求标准化而去不断强调“标准”这个概念,甚至花费大量的时间精力去专注在“标准”们上也是很荒唐的。所以无论我们使用哪些“标准”,参考各大开发团队的标准,形成一套适合自己团队的开发规范即可,内容永远大于形式。

这不是一篇Doxygen的使用教学,而是想推荐类似Doxygen这种代码文档注释管理工具,对于如何使用Doxygen,强烈建议参考官方教程:

Doxygen Manual: Overview​www.doxygen.nl

因为Doxygen对我和我们团队的开发提供了很大的帮助,因此推荐,祝开发愉快:D

发表评论

邮箱地址不会被公开。 必填项已用*标注

Protected with IP Blacklist CloudIP Blacklist Cloud