把注释放到成员之后
这里出现了新词"成员(member)", 在官方手册里也是直接就用了这个词, 而没有给出定义. 我们已经知道, 结构体, 枚举等类型内部声明的变量被称为 member, 而根据官方手册给出的例子来看, 全局变量也被当作 member. 把全局变量看成是文件的 member, 也说得通.
在前面的介绍中, 我们都是把注释放在函数之前的, 而对于结构体, 联合, 枚举这些复合类型, 把注释放到它们的成员的后面或许会更方便.
对于变量, 我们通常也喜欢把注释放到定义的后边. 要做到这一点, 语法很简单, 只要在前面注释的基础上, 多加一个小于号 <
就成了, 例如:
int var1; /*!< Detailed description after the member in Qt style */
int var2; /**< Detailed description after the member in Javadoc style */
int var3; //!< Detailed description after the member
//!<
同样, 如果希望注释内容被当作 brief, 换成只有一行的单行注释即可:
int var; //!< Brief description after the member
int var; ///< Brief description after the member
举个例子, 下面定义了两个全局变量, 其中一个注释在定义前, 而另一个注释在定义后:
/// Brief before global variable
int var1;
int var2; ///< Brief after a global variable;
输出如下:
这种方法也可以用来注释函数的参数, 例如:
/** A func to test inline documentation. */
void many_params(
int a, ///< [in] pass in a
int b, ///< [in] pass in b
int *c ///< [out] pass out c
);
其中 [in]
, [out]
表示函数参数的数值传递的方向, 用法与 @param
命令相同. 这个注释输出成网页是这个样子的:
上述注释除了多了 <
符号之外, 和前文介绍的 special comment block 的含义和结构是一样的. 但是有一个限制, 就是只能用于注释成员(members) 和 参数(parameters), files, classes, unions 等类型自身不能使用这种注释.
注释在头文件和源文件的分布
通常将 C 语言的代码组织成头文件和源文件两部分,头文件中放各种声明,源文件放定义。我们希望可以通过头文件快速浏览有哪些可用的接口,而对于源文件的函数定义,我们则希望可以看到详细的说明。因此,为了保持头文件紧凑,我们可以只在头文件里写 brief,提供必要的简介,而在源文件里写 detail,从而开发者可以在研究函数实现的同时,方便地看到详细的介绍。
Structural commands
把注释放到(几乎)任意位置
目前为止, 我们已经知道如何把注释块放到被注释的 entity 的前边或后边, 而 doxygen 实际上支持把注释块放到几乎任何地方(除了函数体内或普通的C注释内).
在 Special commands 中, 有一类命令称为 Structural Commands, 利用这类指令, 我们可以在一处写代码, 而把注释放到很远的其他地方, 甚至可以是另一个文件. 例如
/** @fn fun()
A function at another file.
*/
这里, @fn
命令指出这个注释的内容是函数 fun()
的文档. 其他的 structural commands 如下:
命令 | 使用对象 |
---|---|
@struct | 结构体 |
@union | 联合 |
@enum | 枚举 |
@fn | 函数 |
@var | 变量, typedef 或 enum 的值 |
@def | #define |
@typedef | 类型定义 |
@namespace | 命名空间 |
@package | Java package |
@interface | IDL interface |
下面是官方手册上, 一个具体的例子(简洁起见, 做了些删改):
/*! @file structcmd.h
@brief A Documented file.
Details.
*/
/*! @def MAX(a,b)
@brief A macro that returns the maximum of \a a and \a b.
Details.
*/
/*! @var typedef unsigned int UINT32
@brief A type definition for a .
Details.
*/
/*! @var int errno
@brief Contains the last error code.
@warning Not thread safe!
*/
/*! @fn int open(const char *pathname,int flags)
@brief Opens a file descriptor.
@param pathname The name of the descriptor.
@param flags Opening flags.
*/
/*! @fn int close(int fd)
@brief Closes the file descriptor \a fd.
@param fd The descriptor to close.
*/
#define MAX(a,b) (((a)>(b))?(a):(b))
typedef unsigned int UINT32;
int errno;
int open(const char *,int);
int close(int);
也可以把本例的注释放到别的文件中. 如果文件扩展名是 .dox, .txt, .doc, .md, 默认这些文件会被 doxygen 读取, 但不会显示在文件列表中, 所以可以考虑把本例中的注释放到 .dox 或 .txt 文件中, 以保持输出文档的整洁.
值得注意的是, 采用 structural commands 注释, 要多写一次变量定义, 函数原型等, 所以后续修改代码时, 要同时修改两处(代码和注释). 而且, 把注释放在远离被注释的 entity 的地方, 不太方便查看. 因此, 除非有什么特殊的理由迫使你这样做, 否则采用 structural commands 通常不是个好主意.
只有先注释高层级的结构, 才会显示低层级的注释
前面也已经提到过, 在默认配置下要在输出的文档中显示注释的内容, 代码中至少要有一句 /** @file */
. 这里把官方手册的原文摘录如下:
To document a member of a C++ class, you must also document the class itself. The same holds for namespaces. To document a global C function, typedef, enum or preprocessor definition you must first document the file that contains it (usually this will be a header file, because that file contains the information that is exported to other source files).
大意是, 要注释一个 C++ 类的成员, 必须先注释这个类它本身. 要注释一个全局的 C 函数, typedef, enum 或 #define, 也必须先注释这个文件.
在注释全局对象(函数, typedef, enum等), 必须先注释这些对象所在的文件这一点常常被忽略, 所以再次提醒.