Skip to content

Writing Documentation

Lydia Buntrock edited this page Oct 6, 2021 · 4 revisions

Rules

  1. Document everything. At least with at a \brief description.
  2. Use British English.

Style

  • Qt-Style, i.e. documentation begins with //! or /*!, not /// or /**
  • Commands begin with \, not with @
  • Use Markdown instead of doxygen specific constructs whenever possible! (E.G. ### instead of \par)

User documentation vs developer documentation

There are two "documentation targets", one for users of the library and one for developers of the library. See Setting up Documentation on how to build these targets.

Usually everything in the namespace seqan3, seqan3::view and seqan3::literal shall be documented for the user and all documentation in these namespaces is visible to the user by default via the instructions above.

On the other hand all documentation in namespace seqan3::detail, all private class members (static or not) and all code inside an #ifndef NDEBUG block, are only included in the developer documentation (which is also the desired behaviour).

In the following situations you might want to restrict something to the developer documentation that would be in the user documentation by default:

  • protected class members: often these are implementation detail, because the inheritance is only relevant to library developers. In this case insert a \privatesection command at the beginning of the section in the code (this can be reverted by inserting a \publicsection later again).
  • public non-static class data members of POD types: as per Classes and Structs PODTypes are preferable in many situations, but they don't allow you to make data members private. In these cases where you also gave a data member a _ prefix, you should also hide it from the user documentation by \privatesection / \publicsection
  • any other more sophisticated documentation aimed at developers, e.g. "When sub-classing the CRTP base class, remember to...": Wrap the region in \cond DEV followed by \endcond later.

Annotating general

The order of entries in a documentation block shall be

  1. \TYPE (e.g \file, \class ...) [this is only required for \file and if the definition is otherwise excluded]
  2. \brief Brief description. [everything should always have this; ENDS WITH A FULL STOP]
  3. \ingroup (SUB-)MODULENAME [every non-member shall provide this]
  4. \implements CONCEPT [only structs/classes have this]
  5. \author FIRST_NAME SECOND_NAME EMAIL [only files should have this]
  6. \tparam example_type Description of template parameter. [Only templates have this. Should contain any constraints on the parameter, e.g. Must satisfy seqan3::alphabet_concept.]
  7. \param[in] foo Description of foo parameters. [Only functions have this, choose [in] or [out] or [in,out]]
  8. \details One-line detail comment. OR

\details

Multi-line
Detailed description.

Annotating classes

When documenting a class, make sure to group the members:

  1. Member types
  2. Constructors, destructor and assignment
  3. Comparison operators (if these behave as expected, it is sufficient to document only the group, not each operator!)

For structs that implement something a range, choose the same member groups as in the documentation on cppreference, e.g. std::vector:

  1. Element access
  2. Iterators
  3. Capacity
  4. Modifiers

Annotating functions

All functions shall additionally provide the following paragraphs in their \details section :

  1. ### Complexity A statement on the complexity, with an O-notation term, e.g. (\f$O(1)\f$).
  2. ### Exceptions [The level of exception safety (see Exceptions). Independent of this any user defined exceptions possibly thrown, shall be annotated with \throws before the \details section]
  3. ### Thread safety The level of thread safety (see Thread Safety).

For member functions of a class the Thread safety can be documented once inside the class, unless if the behaviour of a function is unexpected in this context of course.

Handling shortcomings of Doxygen

Concepts

Doxygen doesn't handle concepts at all, the current behaviour is:

  • (variable) concept definition: Should be escaped completely and documented as an interface.
  • constrained templates: are usually handled ok, an exception are local definitions with requires requires which can cause problems and should be escaped with \cond ... \endcond
  • constrained function templates: to prevent the entire requires clause from being added to the return type, move the required clause between function signature and body (instead of between template header and signature)

See Document Concepts for how to document concept definitions.

Examples:

//!\brief Value metafunction that returns the `value_size` defined inside an alphabet type.
//!\tparam alphabet_type Must provide a `value_size` static member.
template <typename alphabet_type>
//!\cond
    requires requires (alphabet_type c) { alphabet_type::value_size; }
//!\endcond
struct alphabet_size
{
    //!\brief The alphabet's size.
    static constexpr underlying_rank_t<alphabet_type> value = alphabet_type::value_size;
};


# Documenting Concepts

A good workaround for doxygen not supporting concepts seems to be excluding them from the documentation build via `\cond` and `\nocond` and then just defining them via the [interface command](http://www.stack.nl/~dimitri/doxygen/manual/commands.html#cmdinterface). It is also possible to define the required functions on this concept as members or related functions of that concept.

Then we can also mark other types as `\implements concept_name` and have concepts that refine other concept specify `\extends other_concept`. This should provide for really nice documentation!

Here is an example of the alphabet_concept:
```cpp
/*!\interface seqan3::alphabet_concept <>
 * \brief The generic alphabet concept that covers most data types used in ranges.
 * \ingroup alphabet
 *
 * The requirements for this concept are given as related functions and metafunctions.
 * Types that satisfy this concept are shown as "implementing this interface".
 */
/*!\fn auto seqan3::to_char(alphabet_concept const c)
 * \brief Returns the alphabet letter's value in character representation.
 * \relates seqan3::alphabet_concept
 * \param c The alphabet letter that you wish to convert to char.
 * \returns The letter's value in the alphabet's char type (seqan3::underlying_char).
 * ...
 */
//!\cond
template <typename t>
concept bool alphabet_concept = requires (t t1, t t2)
{
    // StL concepts
    requires std::is_pod_v<t> == true;
    requires std::is_swappable_v<t> == true;

    // static data members
    alphabet_size<t>::value;

    // conversion to char and rank
    { to_char(t1) } -> underlying_char_t<t>;
    { to_rank(t1) } -> underlying_rank_t<t>;
    { std::cout << t1 };

    // assignment from char and rank
    { assign_char(t1,  0) } -> t &;
    { assign_rank(t1,  0) } -> t &;
    { assign_char(t{}, 0) } -> t &&;
    { assign_rank(t{}, 0) } -> t &&;

    // required comparison operators
    { t1 == t2 } -> bool;
    { t1 != t2 } -> bool;
    { t1 <  t2 } -> bool;
    { t1 >  t2 } -> bool;
    { t1 <= t2 } -> bool;
    { t1 >= t2 } -> bool;
};
//!\endcond

Source: https://github.com/seqan/seqan3/issues/72

Clone this wiki locally