New Age C++

Documenting C++ APIs with Doxygen

Documenting your code is a critical part of development, but too many developers pay too little attention to proper commenting. C++ devs have a tool that can help.

Javadoc is a standard tool that generates API documentation from its Java source code. It scans the sources to search for comments in a specific enhanced syntax. By commenting code this way, developers keep the API documentation updated.

Here's an example of the Javadoc commenting system:

/**
 * Issues a series of barks.
 * @param times the number of times to bark.
 * @return true if all barks were successful.
 */
boolean bark(int times);

While the C++ standard doesn't have an official API-documenting mechanism like Javadoc, the indie tool Doxygen has deservedly became a de facto standard. I'll show how it can help you code better.

Meet Doxygen
Doxygen is a mature tool, equivalent to Javadoc. It can be used from its graphical wizard, from the command line or as part of a make process.

If you're familiar with Javadoc or the Visual Studio XML documenting mechanism, you'll easily master Doxygen.

Here's the Javadoc example, using Doxygen syntax:

/// Issues a series of barks.
/// @param times the number of times to bark.
/// @return true if all barks were successful.
boolean bark(int times);

The code download shows the same thing in its Java, C# and C++ versions. Doxygen is extremely flexible, allowing comment enhancement in a multitude of ways. You can use syntax from Javadoc, the Microsoft .NET Framework, Qt or Doxygen.

You have a choice between single- and multi-line comments; I recommend single-line, as it enables comments to be embedded in a temporarily commented block.

Listing 1 shows an example of disruptive and non-disruptive ways to compose multi-line comments.

Listing 1. There's more than one way to handle multi-line comments.

// A non-disrupting way to place Doxygen comments.
/*
  ...
  /// Issues a series of barks.
  /// @param times the number of times to bark.
  /// @return true if all barks were successful.
  ...
*/

// A disrupting way.
/*
  ...
  /** Issues a series of barks.
   * @param times the number of times to bark.
   * @return true if all barks were successful.
   */ // (Multi-line comment just ended)
  ...
*/

In Javadoc, the first commented sentence serves as a brief description, while the remaining sentences are more detailed. Thus, the brief description goes to the output summary, together with a link to the detailed information.

In Doxygen, the brief description must instead be explicitly declared (similar to the <summary> element in the .NET Framework). However, setting the configuration option JAVADOC_AUTOBRIEF to YES makes Doxygen act like Javadoc. I did this when creating the companion sample, so you can see how Doxygen and Javadoc produce similar results.

HTML isn't the only format that Doxygen can generate. It can output, among other options, PDF, RTF, XML, Compiled HTML, Unix man pages, and even documentation formats for Eclipse and Xcode.

Compared with Javadoc, Doxygen has a lightweight syntax convention to produce HTML structures such as lists or tables. In Java, I'd have to explicitly embed HTML. Doxygen is more forgiving, as seen in this example:

/// The following text table produces a full HTML table with its
/// first column centered, and the last column left aligned.
/// 
/// | Return Code | Meaning                |
/// | :---------: | :--------------------- |
/// | 0           | Success                |
/// | 10          | Unexpected end-of-file |
/// | 20          | Communication error    |
/// | 30          | Invalid input          |

You can customize the output as well; for instance, you can apply your own style (with, for example, a company logo).

Doxygen is lacking when it comes to tool support: IDE plug-ins are outdated or nonexistent. However, it can still be run as part of a make process or through the wizard -- the generation of the documentation doesn't need to be part of the same compilation process.

Top Documentation Tips
Doxygen is a powerful API documentation generator, but even the best tool won't help if you don't know how, and what, to document. Here are some tips for writing compelling documentation:

  • Be aware that people read documentation as a last resort, and to solve a problem. Because of that, make yours accountable and actionable.
  • Don't talk about design decisions or implementation details, except when warning developers about issues like performance or security -- for instance, whether a class instance function is thread-safe and whether a component logs traces by default (and how to turn logging off).
  • Be careful of pursuing brevity for its own sake. As Einstein said, "Everything should be made as simple as possible, but not simpler."
  • Namespace documentation is a better place to explicate public architecture. This is the set of exposed API components, not their internal collaborators.
  • If your API is still evolving and its public components aren't yet consolidated, keep your documentation to a minimum. Make clear that it's subject to change. Put more weight in the documentation as class contracts become firm.
  • Take the occasional opportunity to revise whether classes and functions need to be public. Be a miser in that sense, exposing only what your user stories justify. Unnecessary flexibility requires more work for both those who write interface documentation and those who read it.
  • Like source code, documentation must also be subject to peer review, ensuring that it's purposeful. Be open to constructive criticism and willing to improve your documentation where appropriate. It should also be a reviewer's responsibility to confirm that an API change is reflected in the documentation.
  • When documenting a deprecated function or class, remember to link to the replacement. This link itself must be documented.
  • Consider code snippets, embedded Unified Modeling Language (UML) diagrams or even small samples to illustrate how components play together. This applies especially to classes with purely virtual functions and any other component that's meant to be derived.
  • Read the documentation of the external APIs you use most and critique it. Make a generic list of what you would've done differently, and make that list a checklist for your own documentation.

Good Documentation Equals Good Communication
You can leverage C++ to write the core libraries for a cross-platform application with platform-specific UIs (Cocoa, Android and so on). For that to happen, you must document your libraries to help platform-specific development teams understand them. Doxygen, like many Javadoc-like tools, helps you keep your API documentation updated because your comments go beside your code. That way, it's easier to confirm that your comments reflect what the code does -- and if not, to properly update it.

Remember also that technical skills don't guarantee good API documentation: communication skills are fundamental. Like other skills, you can -- and should -- develop them.

Other resources include "Best Practices for Documenting Technical Procedures" and these best practices for Agile processes. Ultimately, practicing and developing proper habits will result in the sort of documentation your peers will be happy to read.

About the Author

Diego Dagum is a software architect and developer with more than 20 years of experience. He can be reached at [email protected].

comments powered by Disqus

Featured

Subscribe on YouTube