views:

5236

answers:

15

My team is starting to document our C code using doxygen, paying particular attention to our public API headers. There appears to be a lot of flexibility and different special commands in doxygen, which is great, but it's not clear what's a good thing and what's a bad thing without trial and error.

What are your favourite ways to mark up your code, what are your MUST DOs and DO NOTs?
Please provide your top tips, one per answer to facilitate voting.

I am looking to define our whole approach to API documentation, including providing a template to get the rest of the team started. So far I have something like this:

/**
 * @file   example_action.h
 * @Author Me ([email protected])
 * @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);
+10  A: 

Some commands i use in my code :

  • \todo { paragraph describing what is to be done } Useful to keep track of todos, a page will be created in final documentation containing your todo list.
  • \c <word> Displays the argument using a typewriter font. Use this to refer to a word of code. I would use it before "TRUE" and "FALSE" in your example.
  • \a , \warning , \see : see http://www.stack.nl/~dimitri/doxygen/commands.html#cmdc for description
kraymer
Thanks, some useful stuff there. \c doesn't work for TRUE/FALSE in the question because it's using retval, not return. All your retvals make a table where the first word after the command is the return value, so using \c just gives you a table of "\c".
Andrew Johnson
+4  A: 

For complex projects it may be useful to have a separate file for module management, which controls the groups and subgroups. The whole hierarchy can be in one place and then each file can simply stuff to the child groups. e.g.:

/**
 * @defgroup example Top Level Example Group
 * @brief    The Example module.
 *
 * @{
 */

/**
 * @defgroup example_child1 First Child of Example
 * @brief    1st of 2 example children.
 */

/**
 * @defgroup example_child2 Second Child of Example
 * @brief    2nd of 2 example children.
 */

// @}

Simply including the definition of a group within the { } of another group makes it a child of that group. Then in the code and header files functions can just be tagged as part of whatever group they are in and it all just works in the finished documentation. It makes refactoring the documentation to match the refactor code much easier.

Andrew Johnson
+8  A: 

If you have bugs located in the code or you find bugs you can also tag in the code like this:

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

When you then run doxygen you get a seperate Bug List alongside lists like Todo List

eaanon01
+7  A: 

If you are sure your team will follow such a heavyweight template, fine, use it as shown.

Otherwise, it looks like JavaDoc. One of the nice things about Doxygen is how good a job it does without having to use use such strong markup. You don't need to use @name and with the JAVADOC_AUTOBRIEF setting you can skip @brief - just make sure the first line of the comment is a reasonable brief description.

I prefer descriptive names over enforcing documentation and encouraging people to add comments only when they add significant value. That way, the valuable comments aren't drowned out by all the noise.

Andy Dent
Inevitably if you try to follow a template, you will get a lot of comments like "This is a brief description of the class" in actual code from people forgetting to go back and add stuff intot he comments. Better nothing than these types of comments.
Greg Rogers
The one sad thing about making documentation optional is that, in my experience, code just doesn't get documented. If the template ends up being excessively larger that what most documentation blocks actually are, you can always change it.
axs6791
If you make documentation optional you have the chance to audit if it was added, particularly a quick check with Doxygen changing settings to warn about undocumented classes. I'd also suggest using XML output from Doxygen so you can parse comments for common bland strings.
Andy Dent
We try not to have too large a public API and we have strong code reviews so people are following the templates and the comments are reasonable. The templates we're using aren't as heavyweight as the one above. One big block per API header and a lightweight one per function.
Andrew Johnson
+10  A: 

Use Groups to organise your code into modules.

Remember that you can put almost everything into multiple groups so they can be used to provide semantic tagging like the tags in Stack Overflow. For example, you might tag things as specific to a given platform.

You can also use groups to match a folder hierarchy within an IDE, as shown in my RB2Doxy sample output.

Groups work well when nested - I have a large example for the OOFILE source.

Andy Dent
Yes, using nested groups was what I was talking about with one of my answers. I've been experimenting with using a separate file for the hierarchy management, which seems to work well.
Andrew Johnson
+8  A: 

If you have a really, really big project -- big enough that Doxygen runs take over an hour -- you can cut it up into multiple modules that Doxygen later links together using tag files.

For example, if you have a big MSVC solution with twenty projects in it, you can make directory be its own Doxygen run, and then use tag-files to glue together the output the same way a linker glues together .libs to make an executable.

You can even take the linking metaphor more literally and make each Doxy config file correspond to a .vcproj file, so that each project (eg .lib or .dll) gets its own Doxy output.

Crashworks
This is a good operational tip, albeit one that I've not had to apply in person. If you have used tag files, does that affect HOW people write their Doxygen content in any way that programmers would need to be warned to follow?
Andy Dent
Not really that I've noticed; in principle I suppose you could really treat each tagged piece as its own module and give it its own front page, but we just document as usual and count on Doxygen to link everything together in the end.
Crashworks
+16  A: 

Write a descriptive home page using @mainpage (in a separate header file just for this purpose). Consider, as shown in my example, making it a guide to your main classes/functions and modules.

Andy Dent
+5  A: 

As I find myself editing code on higher-resolution screens I've moved from using the backslash to the @ prefix on Doxygen commands. Not so noisy backslash has found itself now too damned hard to make out the Doxygen commands.

Andy Dent
+4  A: 

I use a subversion post-commit hook to pull out the directories that have changed, write them to a file and then every night I automatically re-generate the doxygen html on our webserver so we always have up-to-date docco.

Every project I want documented has a little project.doxy file that contains the per-project settings and an include to the main doxygen settings - eg:

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"

For Windows SVN server, use the hook:

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

and then run this nightly:

@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

this removes duplicate entries, finds all projects that have a .doxy project file, and runs doxygen on them. Voila: fully documented, always up-to-date code on a webserver.

gbjbaanb
**What is the purpose of INPUT in your file?**
Masi
It seems to be the location of your project.
Masi
**Does the following include some default settings for your doxygen?** c:\dev\CommonConfig.doxy
Masi
My comment was an example. You can read up on doxygen's options here: http://www.stack.nl/~dimitri/doxygen/config.html
gbjbaanb
+10  A: 

A good "best practice" (though not always achievable) is to provide short, working examples for every API, and pull them into the help using \includelineno (or \include for no line numbers). These can be unit tests, if they're written so users can understand them (ie, not hooked into a larger test harness). As a nice side effect, changes to the API will break the samples, so they have to be kept up to date.

You can describe an API in words, but there's nothing like seeing the actual code to understand how to use it.

Hoser
Yeah, I had that in the example template. It's especially useful in the comment that concerns a group of APIs to show how they work together.
Andrew Johnson
+3  A: 

Uses lots and lots of links. This can be done using see also links (\see or @see if you prefer), and making sure that you use any references to other class names in documentation by their correct class name. For example if you refer to class FUZZYObject as an "object", then write immediately after it the name of the class (e.g. "frazzle the objects (FUZZYObject)").

cdiggins
+1  A: 

Group your member functions and fields if it makes sense to do so with \defgroup. This is very helpful, even if you don't say much.

cdiggins
+1  A: 

Always include a description with your classes. Try to say how a class is used, or why it is used, not just what it is (which usually just reflects the name anyway).

cdiggins
A: 

Automatically build and publish your documentation. As part of automatically building the documentation, pay attention to the warnings, its very easy to write badly structure doxygen comments.

John Naegle
A: 

This question and the responses have been very helpful in creating a first draft of a best practices document for writing code comment and doxygen that I am hoping to use for some of our developers at Autodesk. I have made the document public via this blog post

cdiggins