Skip to content

How to document our code

Yunior C. Fonseca Reyna edited this page Jan 28, 2021 · 6 revisions

How to document our code

Doxygen is a tool that parses the sources and generates our documentation. The documentation is written within code, and is thus relatively easy to keep up to date. Doxygen can cross reference documentation and code, so that the reader of a document can easily refer to the actual code.

On this page we will explain:

  1. How to put comments in our code such that doxygen incorporates them in the documentation it generates.
  2. Ways to structure the contents of a comment block such that the output looks good.

We document C++ code in two ways:

  1. By writing documentation blocks for all public or protected C++ components (namespaces, types, methods, functions, and constants).

  2. By commenting our code internally with C++ comments (// or /* .. */).

    These comments are meant to be read only by developers reading and editing the source code.

In general, this means documentation blocks will appear in header (.h) files rather than source (.cpp) files. This keeps all the documentation with the API and avoids certain false alarms when Doxygen parses C++11 code.

For example:

/**
 * Sum numbers in a vector.
 *
 * @param values Container whose values are summed.
 * @return sum of `values`, or 0.0 if `values` is empty.
*/
double sum(std::vector<double> & const values) {
    ...
}

The @defgroup command is used to declare new groups or modules. Think “define group”. The syntax of this command is as follows:

@defgroup <group name> <group description>

The group name is the name used by Doxygen elsewhere to reference this group. The group description is what is displayed when the end user navigates to this module in the resulting documentation. The group description is a couple words formatted as how it would be in a table of contents. This part is what actually shows up in the documentation, when the user navigates to this group’s module, this description will be the modules name.

Important

Groups should only be declared (@defgroup) in .h files.

The @ingroup command is used to add ‘things’ to already declared groups or modules. These ‘things’ can either be other groups, or files themselves. The syntax of the @ingroup command is as follows:

@ingroup <group name>

Using the @ingroup command is how we add ‘’meaning’’ to the ‘’structure’’ created by using @defgroup. @ingroup associates the file it is found in and all other Doxygen found within (function annotations, prototypes, etc) with the group we declared with the @defgroup command. We add related files and headers to the same groups to create a logical and cohesive body of documentation.

By default, we have the following groups and subgroups declared:

/// @defgroup Libraries Libraries
/// @defgroup Programs Programs

/// @defgroup DataLibrary Basic data library
/// @ingroup Libraries

/// @defgroup ReconsLibrary Reconstruction library
/// @ingroup Libraries

/// @defgroup ClassificationLibrary Classification library
/// @ingroup Libraries

/// @defgroup GraphicsLibrary Graphics library
/// @ingroup Libraries

/// @defgroup InterfaceLibrary Interface library
/// @ingroup Libraries

/// @defgroup BilibLibrary Bilib library
/// @ingroup Libraries

/// @defgroup ParallelLibrary Parallel library
/// @ingroup Libraries

/// @defgroup DimRedLibrary Dimensionality reduction library
/// @ingroup Libraries

When we generate our documentation, it will be organised as shown in the following link: xmipp Modules

We can also create groups and subgroups in our code.

For example:

We have geometry.h file, then we can document the code as follow:

/// @defgroup Geometry Geometry
/// @ingroup DataLibrary
//@{
/// @name Geometrical operations
/// @{
 ...
 ...
 ...
 ...
//@}
//@}

This means that the geometry.h belongs to the Geometry group, which in this case is not defined by default, and that this in turn is whitin the DataLibrary group, which is defined by default. This is shown in the documentation: Geometry

A series of @tparam tags, usually one for each template parameter. Each tag should have a description following the parameter name. You do not usually need to document default values; Doxygen will provide the default automatically. If the description extends over multiple lines, each line after the first must be indented.

Parameters should be listed in the same order as they appear in the class, function, or method signature.

An example:

/**
 * Storage for arbitrary data with log(N) lookup.
 *
 * ...
 *
 * @tparam T the type of data stored in the table
 * @tparam ComparatorT callable defining a strict weak ordering for objects
 *     of type `T`. Its `operator()` must accept two `T` and return `true`
 *     if and only if the first argument comes before the second. It must
 *     not throw exceptions.
 */
template <typename T, typename ComparatorT = std::less<T>>
class LookupTable
{
    ...
}

A series of @param tags, usually one for each parameter. Each tag should have a description following the parameter name. You do not usually need to document default arguments; Doxygen will provide the default automatically. If the description extends over multiple lines, each line after the first must be indented.

Parameters should be listed in the same order as they appear in the function or method signature. Make sure to keep the parameter list in sync with the actual parameters; Doxygen will issue a warning if they don’t match.

@param should be given with the [in], [out], or [in, out] tag if the function method contains any output parameters. The [in] tag is optional if all parameters are input, even if other functions or methods in the same class or package use output parameters.

An example:

/**
 * Compute mean and standard deviation for a collection of data.
 *
 * @param[out] mean the mean of `data`, or `NaN` if `data` is empty
 * @param[out] stdDev the unbiased (sample) standard deviation, or `NaN`
 *     if `data` contains fewer than 2 elements
 * @param[in] data the data to analyze
 */
void computeStatistics(double & mean, double & stdDev, std::vector<double> const & data);

Class documentation blocks are placed immediately before the class declaration, and serve to document the class as a whole rather than individual methods.

An example:

/**
 * Implementation of a trace facility for LSST
 *
 * Tracing is controlled on a per "component" basis, where a "component" is a
 * name of the form aaa.bbb.ccc where aaa is the Most significant part; for
 * example, the utilities library might be called "utils", the doubly-linked
 * list "utils.dlist", and the code to destroy a list "utils.dlist.del"
 *
 */
class TraceImpl
{
    public:
        ...
}

All public or protected methods and all functions must be preceded by a documentation block.

An example:

/**
 * Read an image from disk.
 *
 * @param fileName the file to read. Must be either absolute or relative to
 *     the program working directory.
 *
 * @return the image stored in `fileName`. If the image on disk does not
 *     have `double` pixels, they will be cast to `double`.
 *
 * @throws IoError Thrown if `fileName` does not exist or is not readable.
 *
 * @exceptsafe Strong exception guarantee.
 */
lsst::afw::image::Image<double> loadImage(std::string const & fileName);

All non-private constants, variables, or data members must be preceded by a documentation block. At minimum, constants/variables/data members should have a summary line.

For example:

/// Flag set if background subtraction should not be done.
const int NO_BACKGROUND = 1 << 3;

or

/**
 * Flag set if background subtraction should not be done.
 */
const int NO_BACKGROUND = 1 << 3;

For Python there is a standard way of documenting the code using so called documentation strings ("""). Such strings are stored in doc and can be retrieved at runtime. Doxygen will extract such comments and assume they have to be represented in a preformatted way.

 """@package docstring
    Documentation for this module.

    More details.
 """

def func():
    """Documentation for a function.
       More details.
    """
    pass

class PyClass:
    """Documentation for a class.

       More details.
    """

   def __init__(self):
        """The constructor."""
       self._memVar = 0;

   def PyMethod(self):
       """Documentation for a method."""
       pass
Clone this wiki locally