Doxygen style guide#

pigweed.dev uses Doxygen to auto-generate C and C++ API references from code comments in header files. These specially formatted comments are called Doxygen comments. This style guide shows you how to format Doxygen comments and other related documentation.

Quickstart#

Auto-generating an API reference on pigweed.dev requires interacting with a few different tools. This section provides an overview of when you interact with each tool, using pw_i2c as an example.

  1. Annotate your header file using Doxygen syntax. All of the comments that start with triple slashes (///) are Doxygen comments. Doxygen ignores double slash (//) comments.

    Example: //pw_i2c/public/pw_i2c/device.h

  2. Include the API reference content into a reStructuredText file using Breathe directives. Breathe is the bridge between Doxygen and Sphinx, the documentation generator that powers pigweed.dev. See Overview for more explanation.

    Example: //pw_i2c/reference.rst

  3. Add your header file’s path to the _doxygen_input_files list in //docs/BUILD.gn. The docs build system throws a “symbol not found” errors if you forget this step.

    Example: //docs/BUILD.gn

API reference writing style#

Follow the guidance in API reference code comments.

Doxygen comment style#

This section explains how you should style the comments within header files that Doxygen converts into API references.

Comment syntax#

Use the triple slash (///) syntax.

Yes

/// ...
/// ...

Special command syntax#

Special commands like @code and @param are the core annotation tools of Doxygen. Doxygen recognizes words that begin with either backslashes (\) or at symbols (@) as special commands. For example, \code and @code are synonyms. Always use the at symbol (@) format.

Yes

/// @param[out] dest The memory area to copy to.

No

/// \param dest The memory area to copy to.

Structural commands#

Doxygen structural commands like @struct, @fn, @class, and @file associate a comment to a symbol. Don’t use structural commands if they’re not needed. In other words, if your Doxygen comment renders correctly without the structural command, don’t use it.

Code examples#

Use @code{.cpp} followed by the code example followed by @endcode.

Yes

/// @code{.cpp}
///   #include "pw_kvs/key_value_store.h"
///   #include "pw_kvs/flash_test_partition.h"
///
///   ...
/// @endcode

Omit or change {.cpp} if your code example isn’t C++ code.

Yes

/// @code
///   START + I2C_ADDRESS + WRITE(0) + TX_BUFFER_BYTES + STOP
///   START + I2C_ADDRESS + READ(1) + RX_BUFFER_BYTES + STOP
/// @endcode

Parameters#

Use @param[<direction>] <name> <description> for each parameter.

Yes

/// @param[out] dest The memory area to copy to.
/// @param[in] src The memory area to copy from.
/// @param[in] n The number of bytes to copy.

Put a newline between the parameters if you need multi-line descriptions.

Yes

/// @param[out] dest Lorem ipsum dolor sit amet, consectetur adipiscing
/// elit, sed do eiusmod tempor incididunt ut labore et dolore magna...
///
/// @param[in] src The memory area to copy from.
///
/// @param[in] n The number of bytes to copy.

The direction annotation is required.

No

/// @param dest The memory area to copy to.
/// @param src The memory area to copy from.
/// @param n The number of bytes to copy.

<direction> must be specified and the value must be either in or out.

Preconditions#

Use @pre <description>.

Yes

/// @pre Description of a precondition that must be satisifed before
/// invoking this function.

Deprecated features#

Use @deprecated <description>.

Yes

/// @deprecated This function, class, or other entity is deprecated. Use
/// the replacement instead.

pw_status codes#

Use the following syntax when referring to pw_status codes:

Yes

@pw_status{...}

Replace ... with a valid pw_status code. See Quick reference.

Doxygen converts this alias into a link to the status code’s reference documentation.

Don’t use this syntax for functions or methods that return a set of status codes. Use pw-status-codes. See Functions and methods that return pw::Status codes.

Functions and methods that return pw::Status codes#

Use pw-status-codes to present the set of codes and descriptions as a two-column table:

Yes

/// @returns @rst
///
/// .. pw-status-codes::
///
///    <code>: <description>
///
///    <code>: <description>
///
/// @endrst

Example:

Yes

/// @returns @rst
///
/// .. pw-status-codes::
///
///    OK: The bulk read was successful.
///
///    RESOURCE_EXHAUSTED: The remaining space is too small to hold a
///    new block.
///
/// @endrst
  • Each <code> must be a valid status code. The part before the colon must be plaintext.

  • Each <description> should explain further what the code means in this particular scenario. The description must be a single paragraph. It can use inline reStructuredText features such as code formatting and cross-references.

  • pw-status-codes needs to be wrapped in @rst and @endrst because it’s a reStructuredText directive and Doxygen doesn’t natively support reST. The implementation is at //pw_docgen/py/pw_docgen/sphinx/pw_status_codes.py.

Yes

/// @returns @rst
///
/// .. pw-status-codes::
///
///    OK: Data successfully written to ``buffer``.
///
///    RESOURCE_EXHAUSTED: The remaining space is too small to hold a
///    new block. See :ref:`module-pw_example-troubleshooting`.
///
/// @endrst

It’s OK to use reStructuredText features like code formatting and cross-references within the descriptions. The status code itself must be plaintext.

No

/// @returns @rst
///
/// .. pw-status-codes::
///
///    RESOURCE_EXHAUSTED: The remaining space is too small to hold a
///                        new block.
///
/// @endrst

For items that span multiple lines, don’t use whitespace like this.

Use fully namespaced names#

In general, write out the full namespace to Pigweed classes, methods, and so on. If you’re writing a code sample, and that code sample clearly shows where the item comes from via a using statement, you don’t need to use full namespacing.

Discussion

Pigweed has over 160 modules. It can be overwhelming for beginners to figure out where an item is coming from.

Single comment block for multiple symbols#

Use @def <symbol> followed by the comment block.

Yes

/// @def PW_ASSERT_EXCLUSIVE_LOCK
/// @def PW_ASSERT_SHARED_LOCK
///
/// Documents functions that dynamically check to see if a lock is held, and
/// fail if it is not held.

Cross-references (x-refs)#

X-refs in Doxygen comments#

For C or C++ x-refs, use one of the following aliases:

Alias

reStructuredText equivalent

@c_macro{<identifier>}

:c:macro:`<identifier>`

@cpp_func{<identifier>}

:cpp:func:`<identifier>`

@cpp_class{<identifier>}

:cpp:class:`<identifier>`

@cpp_type{<identifier>}

:cpp:type:`<identifier>`

For all other x-refs, use Pigweed’s custom basic alias, @crossref{<domain>,<type>,<identifier>}. <domain> must be a valid Sphinx Domain and <type> must be a valid type within that domain. @crossref can be used with any domain.

Avoid Doxygen x-refs#

Always use Pigweed’s custom aliases for x-refs unless you have specific reasons not to do so. Pigweed’s x-ref aliases are built on top of Sphinx. Doxygen provides its own features for x-refs but they should be avoided because Sphinx’s are better:

  • Sphinx x-refs work for all identifiers known to Sphinx, including those documented with directives like .. cpp:class:: or extracted from Python. Doxygen references can only refer to identifiers known to Doxygen.

  • Sphinx x-refs always use consistent formatting. Doxygen x-refs sometimes render as plaintext instead of code-style monospace, or include () in macros that shouldn’t have them.

  • Sphinx x-refs can refer to symbols that haven’t yet been documented. They will be formatted correctly and become links once the symbols are documented.

Using Sphinx x-refs in Doxygen comments makes x-ref syntax more consistent within Doxygen comments and between reStructuredText and Doxygen.

Cross-references in reST to Doxygen symbols#

After you’ve used Doxygen to generate API references, you can link to those symbols from your reStructuredText with standard Sphinx x-ref syntax.

Yes

:cpp:class:`pw::sync::BinarySemaphore::BinarySemaphore`

In the Sphinx docs the reference documentation for x-ref syntax is provided in the domain docs:

Embedded reStructuredText#

To use reStructuredText (reST) within a Doxygen comment, wrap the reST in @rst and @endrst.

Breathe#

This section provides guidance on how Breathe should and shouldn’t be used when authoring pigweed.dev docs.

Overview#

After you annotate your header comments as Doxygen comments, you need to specify where to render the API reference within the pigweed.dev docs. The reStructuredText files distributed across the main Pigweed repo are the source code for pigweed.dev. Updating these .rst files is how you surface the API reference on pigweed.dev. Doxygen doesn’t natively interact with Sphinx, the documentation generator that powers pigweed.dev. Breathe is the bridge and API that enables pigweed.dev and Doxygen to work together.

doxygenclass#

OK to use. doxygenclass documents a class and its members.

Yes

.. doxygenclass:: pw::sync::BinarySemaphore
   :members:

Classes that are a major part of a Pigweed module’s API should have their own section so that they’re easy to find in the right-side page nav on pigweed.dev.

Yes

.. _module-pw_<name>-reference:

=========
Reference
=========
.. pigweed-module-subpage::
   :name: pw_<name>

...

.. _module-pw_<name>-reference-<class>:

-------------------
pw::<name>::<class>
-------------------
.. doxygenclass:: pw::<name>::<class>
   :members:

doxygenfunction#

OK to use. doxygenfunction documents a single function or method.

Yes

.. doxygenfunction:: pw::tokenizer::EncodeArgs

doxygendefine#

OK to use. doxygendefine documents a preprocessor macro.

Yes

.. doxygendefine:: PW_TOKENIZE_STRING

doxygengroup#

doxygengroup lets you manually mark a set of symbols as belonging to the same conceptual group.

doxygengroup is OK to use when a simple doxygenclass-based organization doesn’t work well for your module.

To create a group, annotate your Doxygen comments with @defgroup, @addtogroup, and @ingroup. You can wrap a set of contiguous comments in @{ and @} to indicate that they all belong to a group.

Yes

/// @defgroup <name> <description>
/// @{
/// ...
/// @}

Don’t include namespaces in doxygengroup because Breathe doesn’t handle them correctly. See issue #772.

Yes

.. cpp:namespace:: my_namespace

.. doxygengroup:: my_group
   :content-only:
   :members:

doxygentypedef#

OK to use. doxygentypedef documents a typedef or using statement.

Yes

.. doxygentypedef:: pw::Function

doxygenfile#

Don’t use doxygenfile. Use doxygengroup instead.

Disabled auto-generated #include statements#

Note

This is an FYI section. There’s no prescriptive rule here.

Doxygen and Breathe have the ability to auto-generate #include statements for class references. These have been disabled because:

  • The auto-generated paths are inaccurate. E.g. the #include for pw::string::RandomGenerator was generated as #include <random.h> when it should be #include "pw_random/random.h".

  • The auto-generation is not consistent. They seem to only get generated when using the doxygennamespace directive but pigweed.dev frequently uses doxygenclass, doxygenfunction, etc.

In the future, if it’s decided that these #include statements are needed, there is a way to manually override each one. The code block below shows how it’s done. This approach wouldn’t be great because it adds a lot of noise to the header files.

/// @class RandomGenerator random.h "pw_random/random.h"``

See b/295023422.