使用doxygen记录代码的最佳技巧是什么?

308

我的团队开始使用Doxygen文档记录我们的C代码,尤其关注我们的公共API头文件。 Doxygen具有很大的灵活性和不同的特殊命令,这很好,但是在没有试错的情况下,不清楚什么是好事情,什么是坏事情。

您喜欢如何标记您的代码?您有哪些必须做和禁止做的事项?
请提供您的顶级提示,每个答案一个,以便投票。

我希望定义我们整个API文档的方法,包括提供模板来启动团队的其余部分。 到目前为止,我的想法是这样的:

/**
 * @file   example_action.h
 * @Author Me (me@example.com)
 * @date   September, 2008
 * @brief  Brief description of file.
 *
 * Detailed description of file.
 */

/**
 * @name    Example API Actions
 * @brief   Example actions available.
 * @ingroup example
 *
 * This API provides certain actions as an example.
 *
 * @param [in] repeat  Number of times to do nothing.
 *
 * @retval TRUE   Successfully did nothing.
 * @retval FALSE  Oops, did something.
 *
 * Example Usage:
 * @code
 *    example_nada(3); // Do nothing 3 times.
 * @endcode
 */
boolean example(int repeat);

7
我认为 param[in][out] 部分是不必要的。你的 API 应该说明某个变量是输入变量还是输出变量:const int * const a 是一个输入,int * const a 是一个输出。 - Matt Clarkson
6
除非作者的姓名是版权声明的一部分,否则文件作者不应该出现在源文件中。确定谁写了什么是源代码控制“责任”的功能所在。 - Kuba hasn't forgotten Monica
@Mark,我没有想到这一点(+1)。当然,它不是那么直观,但它不会出现错误同步,所以你说得对。我有时候#define了“INPUT”、“MODIFY”和“OUTPUT”为空宏,并在声明和实际调用中都使用它们。关于这是否是好事意见不一。 - Mawg says reinstate Monica
20个回答

92

不需要并且也不应该在@file指示符中写入文件名,doxygen会自动读取文件名。写文件名的问题是,当您重命名文件时,您还必须更改@file指示符。

大多数情况下提供@author@date信息也是无用的,因为源代码控制系统比手动编辑文件的人做得更好。

如果您使用以下Doxygen语法并启用JAVADOC_AUTOBRIEF,那么您也不必编写@brief

/*! Short Description on the first line

    Detailed description...
    ...
*/
void foo(void) {}

@name 指令对于函数来说通常是 100% 冗余和完全无用的。它只会在某人修改函数名而不是 doxygen @name 时带来错误。


4
Doxygen文档中提到:“让我们重复一遍,因为它经常被忽视:要记录全局对象(函数、typedef、枚举、宏等),必须记录定义它们的文件。换句话说,此文件中必须至少有一个 /! \file / 或 / @file */ 行。” 这似乎暗示\file是必需的,但这可能只是被误解了。值得澄清一下。 - ivo Welch
7
“\file”是必需的,但它不是文件的名称,这就是我想说的 ;) - Étienne
2
请Étienne在您的答案中添加一个使用\file而没有文件名的文件开头的示例。我认为您需要设置JAVADOC_AUTOBRIEF = YES以避免使用@brief。您能确认一下吗?您建议如何为文件提供简要说明?干杯 - oHo
@ivoWelch他们的文档还说:“对于成员函数或属于命名空间的函数,您应该记录类或命名空间。”因此,如果所有自由函数都在命名空间中,\file可能看起来不需要?我没有检查过... - Ela782
如果你的所有函数都在一个类或命名空间中,那么就不需要使用 \file。请记住,我是在回答一个特定问题,该问题在模板中使用了 \file(在我看来这是有意义的)。 - Étienne

60

使用@mainpage(在单独的标题文件中)编写一个描述性首页。考虑将其作为主要类/函数和模块的指南,就像我的示例一样。

另一个示例

当我正在将上面链接的主oo文件的doxygen内容恢复在线时,这里有一个来自某些当前客户工作的Markdown格式示例。使用Markdown,您可以在markdown中引用mainpage(在Doxygen设置中),这对于包含在开源项目中的典型readme.md文件非常有用。

Lingopal
========
Developer Documentation started when Andy Dent took over support in May 2014. 

There are a number of pages in Markdown format which explain key aspects:

- @ref doc/LingopalBuilding.md
- @ref doc/LingopalSigning.md
- @ref doc/LingopalDatabases.md
- @ref doc/LingopalExternals.md

See the <a href="pages.html">Related Pages list for more.</a>

-------------

_Note_

These pages, whilst readable by themselves, are designed to be run through the [Doxygen](http://www.doxygen.com) code documentation engine which builds an entire local cross-referenced set of docs. It uses a minor [extension of Markdown formatting.](http://www.stack.nl/~dimitri/doxygen/manual/markdown.html)

The settings to generate the documentation are `Lingopal.doxy` and `LingopalDocOnly.doxy`. The latter is used for quick re-generation of just these additional pages.

7
链接已经失效,无法查看您的意思。我建议要么将答案扩展为自包含的形式,要么修复这个链接。 - Rick Deckard
1
抱歉,我会尽快恢复它,我的博客遭受了DDOS攻击,因此不得不关闭我的Doxygen网站,因为它在一个目录中有太多的文件。 - Andy Dent

54

使用来将代码组织成模块。

请记住,您可以将几乎所有内容放入多个组中,以便提供语义标记,就像在Stack Overflow中的标记一样。例如,您可能会将某些内容标记为特定于给定平台。

您还可以使用组来匹配IDE中的文件夹层次结构,如我的RB2Doxy示例输出所示。

当嵌套时,组的效果很好 - 我有一个大的示例适用于OOFILE源


是的,使用嵌套组是我在其中一篇答案中所谈论的。我一直在尝试使用单独的文件来管理层次结构,这似乎效果不错。 - Andrew Johnson
2
希望我可以给这个和其他涉及组的答案多一个投票。Doxygen不能告诉你任何不在代码中的信息。但是它可以按不同顺序或过滤器呈现信息。组是您可以添加到注释中的最有用的东西之一,因为它允许doxygen提供您无法通过在编辑器中打开源文件获得的东西 - 对代码进行显着重新组织的视图。 - Bowie Owens
6
存在损坏链接... - Aaron Campbell
我在一个.zip文件中找到了RB2Doxy的输出:http://www.oofile.com.au/files/REALbasic/RB2DoxySample.zip - HotOil
请问您能否修复您回答中的链接? - xaxxon

49

我在我的代码中使用的一些命令:

  • \todo { 描述要做的事情的段落 } 有助于跟踪待办事项,最终文档中将创建一个包含您待办事项列表的页面。
  • \c <word> 使用打字机字体显示参数。用于引用代码单词。在您的示例中,我会在"TRUE"和"FALSE"之前使用它。
  • \a , \warning , \see : 请参见http://www.stack.nl/~dimitri/doxygen/commands.html#cmdc了解详细说明。

2
谢谢,这里有一些有用的东西。在问题中,\c对于TRUE/FALSE是不起作用的,因为它使用的是“retval”,而不是“return”。所有的“retval”都会生成一个表格,在命令后的第一个单词是返回值,所以使用\c只会给你一个"\c"的表格。 - Andrew Johnson

36
一个好的“最佳实践”(虽然并非总是可行)是为每个API提供简短的工作示例,并使用\includelineno(或\include用于无行号)将它们拉入帮助文档中。如果编写的单元测试可以让用户理解它们(即不连接到更大的测试框架),那么这些示例可以是单元测试。一个很好的副作用是,对API的更改将破坏示例,因此必须使其保持最新。

您可以用文字描述API,但没有什么比看到实际代码更能理解如何使用它。


是的,我在示例模板中有这个。它在涉及一组API如何协同工作的注释中特别有用。 - Andrew Johnson

28

随着我在更高分辨率屏幕上编辑代码,我已经开始使用@前缀而非反斜杠来表示Doxygen命令。由于反斜杠过于喧闹,现在难以看清Doxygen命令。


27

如果您确信团队将遵循这样一种沉重的模板,那么请使用它,就像展示的那样。

否则,它看起来像JavaDoc。 Doxygen的一个好处是在不必使用如此强烈的标记的情况下完成得非常出色。您不需要使用@name,并且使用JAVADOC_AUTOBRIEF设置可以跳过@brief - 只需确保注释的第一行是合理的简要描述即可。

我更喜欢具有描述性名称的方式,而不是强制撰写文档,并鼓励人们仅在添加重要价值时才添加注释。这样,有价值的评论不会被所有噪音淹没。


7
如果你试图按照模板进行编写,就不可避免地会在实际代码中遇到很多类似“这是一个简要描述”的注释,因为人们忘记返回并添加内容。没有这些类型的注释比有更好。 - Greg Rogers
1
让文档成为可选项的一个悲哀之处在于,根据我的经验,代码往往不会被记录。如果模板最终比大多数文档块实际上更大,您总是可以进行更改。 - axs6791
4
如果您将文档编写变成可选项,您可以审查是否已添加文档,尤其通过使用Doxygen更改设置以警告未记录的类进行快速检查。我还建议使用Doxygen的XML输出,这样您就可以解析注释以获取常见的平淡字符串。 - Andy Dent
我们尽量避免有过于庞大的公共API,同时我们有强力的代码审查流程来确保每个人都遵循模板并且注释也合理。我们使用的模板没有上述那么笨重,每个API头信息只用一个大块来表示,每个函数则只需要一个轻量级的块即可。 - Andrew Johnson

25

如果你在代码中发现了错误,或者你想标记代码中的错误,可以像这样进行标记:

/** @bug The text explaining the bug */

当你运行doxygen时,你会得到一个独立的Bug列表,以及像Todo列表这样的列表。


15
我使用Subversion的post-commit hook来提取已更改的目录,将它们写入文件,然后每晚自动重新生成我们网站上的Doxygen HTML,以便我们始终拥有最新的文档。
每个我想要记录的项目都有一个小的project.doxy文件,其中包含每个项目的设置和对主Doxygen设置的引用 - 例如:
PROJECT_NAME           = "AlertServer"
PROJECT_NUMBER         = 8.1.2
INPUT                  = "C:/Dev/src/8.1.2/Common/AlertServer"
HTML_OUTPUT            = "AlertServer"
@INCLUDE = "c:\dev\CommonConfig.doxy"

针对 Windows SVN 服务器,请使用以下钩子:

@echo off
for /F "eol=¬ delims=¬" %%A in ('svnlook dirs-changed %1 -r %2') do echo %%A >> c:\svn_exports\export.txt

然后每晚运行此操作:
@echo off

rem ---------------
rem remove duplicates.
type nul> %TEMP%.\TEMP.txt

for /F "eol=¬ delims=¬" %%a in (c:\svn_exports\export.txt) do (
 findstr /L /C:"%%a" < %TEMP%.\TEMP.txt > nul
 if errorlevel=1 echo %%a>> %TEMP%.\TEMP.txt
)

copy /y %TEMP%.\TEMP.txt export_uniq.cmd >nul
if exist %TEMP%.\TEMP.txt del %TEMP%.\TEMP.txt


rem ---------------
rem fetch all the recently changed directories into the svn_exports directory

for /F "eol=¬ delims=¬" %%A in (c:\svn_exports\export_uniq.cmd) do (
  svn export "file:///d:/repos/MyRepo/%%A" "c:/svn_exports/%%A"  --force 
)


rem ---------------
rem search through all dirs for any config files, if found run doxygen

for /R c:\svn_exports %%i in (*.doxy) do c:\tools\doxygen\bin\doxygen.exe "%i"


rem ---------------
rem now remove the directories to be generated.
del /F c:\svn_exports

这将删除重复项,查找所有具有 .doxy 项目文件的项目,并对它们运行 doxygen。嘿,神奇:在Web服务器上有完整记录、始终最新的代码。

你的文件中 INPUT 的目的是什么? - Léo Léopold Hertz 준영
这似乎是您项目的位置。 - Léo Léopold Hertz 준영
以下内容是否包含您的Doxygen的一些默认设置?c:\dev\CommonConfig.doxy - Léo Léopold Hertz 준영
我的评论只是一个例子。你可以在这里阅读doxygen的选项:http://www.stack.nl/~dimitri/doxygen/config.html - gbjbaanb

15

如果你有一个非常非常大的项目——大到Doxygen运行需要超过一个小时——那么你可以将其分成多个模块,后来使用标记文件在 Doxygen 中链接起来

例如,如果你有一个包含二十个项目的大型 MSVC 解决方案,你可以让每个目录都成为自己的 Doxygen 运行,并使用标记文件将输出粘合在一起,就像链接器将 .libs 粘合在一起形成可执行文件一样。

甚至可以更加字面理解地采用链接的比喻,使每个 Doxy 配置文件对应一个 .vcproj 文件,这样每个项目(例如 .lib 或 .dll)都有自己的 Doxy 输出。


这是一个不错的操作提示,虽然我个人没有实际应用过。如果您使用了标记文件,是否会以任何方式影响人们编写Doxygen内容,从而需要警告程序员要遵循的规则? - Andy Dent
我并没有注意到这一点;原则上,我想你可以将每个标记的部分视为自己的模块,并为其提供自己的主页,但我们只是像往常一样进行文档编写,并依靠Doxygen在最终将所有内容链接在一起。 - Crashworks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接