Trilinos Doxygen Guidelines?
Created by: jmgate
@trilinos/framework
Expectations
It'd be great if Trilinos had some general Doxygen guidelines for developers to follow when documenting the code they write. Has this ever been considered before?
Current Behavior
We already have this Doxygen wiki page, but it focusses on how to use Doxygen to automatically generate the documentation for your package. What it doesn't say anything about, though, is what is recommended in terms of documentation in header files of classes, namespaces, functions, data, etc.
I realize Trilinos has historically left matters like this up to the individual packages to decide. Fair enough, but it seems the Trilinos community might be better served if there were some general guidelines from on high that package maintainers could then modify as they see fit.
Motivation and Context
I come across way too much undocumented code on a regular basis. I imagine that's largely due to work being done quick and dirty in a fast-paced research environment—c'est la vie. I'd guess the problem is also compounded by the number of short-term developers (interns/postdocs/etc.) we have working on Trilinos. Left unaddressed, the problem will just continue to get worse.
I'm not saying we should drop everything, go back, and document everything that's been left undocumented over the years—actually I just might say that if you pressed me on it. I am saying it'd be awful nice to have some guidelines in place such that new code that gets added is heading us in the right direction.
Definition of Done
-
Figure out if Trilinos should have such guidelines. -
If so, figure out where they should live. -
Figure out what should be in them. -
Get them out there. -
Point Trilinos developers to them.
Possible Solution
Here are some Doxygen guidelines I whipped up recently for one of the applications I'm on, modified slightly to apply to Trilinos.
Sample Doxygen Guidelines (click to expand)
Trilinos uses Doxygen to generate documentation from annotated source code.
Documenting a Class or Struct
Note: The documentation of a
struct
is the same as that required for aclass
as they are essentially the same thing, differing only in the default visibility of their members.
The Class Itself
Classes should be preceded by a comment block along these lines:
/**
* \brief A brief description of the class goes here. The brief description
* is terminated by a blank line.
*
* The detailed description of the class follows the blank line. This should
* give an unfamiliar developer enough information to understand the purpose
* of the class and how to interact with instantiations of it via the methods
* that will be defined below.
*
* If a detailed description continues over multiple paragraphs, separate
* paragraphs with a blank line.
*/
template<stuff>
class ClassName
:
public BaseClass<stuff>
{
// Insert class definition here.
} // end of class ClassName
Functions
Within the class definition, functions should be preceded by a comment block along these lines:
/**
* \brief A brief description of the function goes here. The brief
* description is terminated by a blank line.
*
* The detailed description of the function follows the blank line. This
* should give an unfamiliar developer an understanding of what the function
* is doing and why you would use it.
*
* \note If anything is noteworthy, feel free to include that here. Perhaps
* you might mention how this compares to another function in the class,
* if it is to be preferred over another method, if it has been
* deprecated and should no longer be used, etc. Similar commands you
* can use in this manner are `\remark` and `\warning`.
*
* \param[in,out] arg1 This is a description of `arg1`. This variable is used
* as both input and output; that is, its value changes
* and persists after the function call.
* \param[out] arg2 This is a description of `arg2`. This variable is used
* as output; its value when the function begins is
* irrelevant.
* \param[in] arg3 This is a description of `arg3`. Be sure to mention
* that it's inclusion is optional, and if omitted, what
* the default value is.
*
* \pre This is some precondition that must be satisfied before the function
* executes.
*
* \post This is some postcondition that will be satisfied after the function
* executes.
*
* \throws ExceptionType This is a description of why `ExceptionType` would be
* thrown in the midst of the function execution.
*
* \returns This is a description of what the function returns on completion,
* if anything.
*/
returnType
functionName(
someType arg1,
anotherType arg2,
yetAnotherType arg3 = someDefaultValue);
If any of the lines in the comment block above (\note
, \warning
, \param
, \throws
, \returns
) are not applicable to the function you're documenting, simply omit them.
Member Data
Member data should be preceded by a comment block along these lines:
/**
* \brief A brief description of the data goes here. The brief description
* is terminated by a blank line.
*
* The detailed description of the data follows the blank line. If the brief
* description gives enough information to understand the variable, its use
* and purpose, then a detailed description may not be necessary.
*/
DataType
member_data_;
Enumerations
When documenting an enum
, use something along the lines of the following:
/**
* \brief A brief description of the enum goes here. The brief
* description is terminated by a blank line.
*
* The detailed description of the enum follows the blank line. This should
* give an unfamiliar developer an understanding of what the enum represents
* and how it is used.
*/
enum EnumName
{
SOMETHING, /*!< This is a description of the SOMETHING value of the enum.
It can be as detailed as you like. */
SOMETHING_ELSE /*!< This is a description of SOMETHING_ELSE. */
}; // end of enum EnumName
Typedefs and Usings
When documenting a typedef
, the syntax is essentially the same as that used for member data:
/**
* \brief A brief description of the typedef goes here. The brief
* description is terminated by a blank line.
*
* The detailed description of the typedef follows the blank line. If the
* brief description gives enough information to understand the typedef, its
* use and purpose, then a detailed description may not be necessary.
*/
typedef OriginalType NewName;
However, as of C++11, we should really be phasing out typedef
s in favor of using
statements. Unfortunately, Doxygen has not been updated to support those out of the box, so it needs a little help. We can use the \var
command to tell Doxygen to document a using
statement as if it were a variable:
/**
* \var NewName
*
* \brief A brief description of the using statement goes here. The brief
* description is terminated by a blank line.
*
* The detailed description of the using statement follows the blank line.
* If the brief description gives enough information to understand the
* using statement, then a detailed description may not be necessary.
*/
using NewName = OriginalType;
Grouping Entities
You may find it useful to group certain functions, variables, etc., together into named sections, particularly if your class contains a great many members. This can aid in understanding the design and intended use of the class. To group entities together in the documentation, use the following:
//-----------------------------------------------------------------------------
/* */
/** \name Group 1 Name */
/** @{ */
//-----------------------------------------------------------------------------
// Insert functions, variables, typedefs, etc., here, along with their
// documentation.
//-----------------------------------------------------------------------------
/** @} */
/* end of Group 1 Name */
/* */
//-----------------------------------------------------------------------------
Be sure not to forget the /** @{ */
and /** @} */
, which open and close the group.
Note: These grouping characters must appear on their own lines. If they're on a line with other non-comment characters, Doxygen won't process them correctly.
Alternatively, if you wish to define a few groups in succession, you could use the following:
//-----------------------------------------------------------------------------
/* */
/** \name Group 1 Name */
/** @{ */
//-----------------------------------------------------------------------------
// Insert functions, variables, typedefs, etc., here, along with their
// documentation.
//-----------------------------------------------------------------------------
/** @} */
/** \name Group 2 Name */
/** @{ */
//-----------------------------------------------------------------------------
// Insert more stuff here, along with its documentation.
//-----------------------------------------------------------------------------
/** @} */
/* end of Group 2 Name */
/* */
//-----------------------------------------------------------------------------
Documenting the Group Itself
If you like, you can include documentation that pertains to all members of a group. To do so, use something along the lines of:
//-------------------------------------------------------------------------------
/**
* \name Group Name
* @{
*
* This is a detailed description that pertains to all the members of this
* group.
*
* \param[in] x - This is an input that pertains to every member of the group.
*
* \returns This is something every member of the group returns, at least in
* some general sense.
*/
//-------------------------------------------------------------------------------
// Insert the members of the group, along with any corresponding documentation.
// You can either document just the first member (see below) or document each
// member separately.
//-----------------------------------------------------------------------------
/** @} */
/* end of Group Name */
/* */
//-----------------------------------------------------------------------------
This documentation will appear between the group name and its members in the brief description section of the automatically generated HTML page, but it will not appear with the detailed documentation of any of the members.
Only Documenting the First Member
If DISTRIBUTE_GROUP_DOC=YES
in your Doxygen configuration, the documentation on the first member of a group will get spread across all members of the group in the generated HTML. This can be useful, for instance, if you have a handful of functions that all do more or less the same thing—you can document them once in the code, but someone using the Doxygen as a reference manual will be able to see that documentation regardless of which function in the group they happen to be looking at.
For instance, if you have
//-----------------------------------------------------------------------------
/* */
/** \name Random Generators */
/** @{ */
//-----------------------------------------------------------------------------
/**
* \brief Get a random variable.
*
* Generate a random `int`/`double`/`char`/`string`.
*/
int rand();
double rand();
char rand();
std::string rand();
//-----------------------------------------------------------------------------
/** @} */
/* end of Random Generators */
/* */
//-----------------------------------------------------------------------------
the generated HTML will be such that it'll look like you copied and pasted the comment before int rand()
in front of the other three routines.
Note: If you do not wish all members of a group to share the same documentation, each and every member must be documented separately.
Documenting a Namespace
Namespaces appear throughout our code, but if documentation were to show up before each occurrence of a namespace, those various bits of documentation will get bundled together by Doxygen in a manner depending on in what order it processes the files. To avoid the potential confusion there, namespaces should be documented in Trilinos/packages/packageName/src/Namespaces.hpp
(or if your package contains subpackages, you could have a Trilinos/packages/packageName/subpackageName/src/Namespaces.hpp
for each subpackage). Each namespace should be preceded by a comment block along these lines:
/**
* \brief A brief description of the namespace goes here. The brief
* description is terminated by a blank line.
*
* The detailed description of the namespace follows the blank line. This
* should give an unfamiliar developer enough information to understand the
* purpose of the namespace, what it contains, why it was organized in such a
* way, etc.
*
* If the detailed description continues over multiple paragraphs, separate
* paragraphs with a blank line.
*/
namespace something
{
} // end of namespace something
Nothing need to be in the namespace in Namespaces.hpp
, other than any nested namespaces and their associated documentation.
General Doxygen Guidelines
The Doxygen manual will tell you everything you need to know about using Doxygen to document your code. Here are some highlights:
Todos
If, for whatever reason, you are unable to complete the documentation of a class, function, variable, etc.—perhaps you need to consult with a coworker to ensure you have an accurate description of what you're documenting—be sure to use the \todo
command to flag this as documentation that still needs work. For instance,
/**
* \brief This function does something really cool.
*
* But I'm not entirely sure what it is yet.
*
* \todo Finish documenting this function.
*/
returnType
awesomeFunction();
If GENERATE_TODOLIST=YES
in your Doxygen configuration, \todo
items populate the "Todo List" page under the "Related Pages" tab of your package's Doxygen site, so it's easy to see what still needs work.
Undocumented Entities
When you run Doxygen, for instance,
cd path/to/Trilinos/packages/packageName/docs
./builddocs.sh |& tee doxygen.log
it will warn you about any undocumented entities. You can search through the output for warnings associated with files you've touched to ensure you haven't missed documenting anything. For instance,
grep warning doxygen.log | grep FileIModified.hpp
Comment Blocks
Note that in the midst of Doxygen-style comment blocks
/**
* Text
* goes
* here.
*/
any leading *
marks are stripped out by Doxygen before any other processing is done.
Automatic Link Generation
Doxygen will automatically generate hyperlinks in a handful of scenarios. When mentioning a function, be sure to include ()
at the end to tell Doxygen to generate a link to that function's documentation, as in
/**
* Check out `functionName()`. It's pretty great.
*/
Markdown Syntax
Doxygen does have support for rendering Markdown syntax.
When it comes to documenting classes, functions, and data as specified above, be sure to use `backticks`
around class names, function names, and snippets of code that appear inline to have them rendered in a monospaced font.
If creating a bulleted list, use non-asterisk bullet markers, as leading asterisks will be stripped away. That is, prefer this
/**
* Here's a list:
* - Item 1
* - Item 2
*/
to this
/**
* Here's a list:
* * Item 1
* * Item 2
*/
Including Math
Doxygen allows you to include mathematical formulas and the like by surrounding LaTeX with certain delimiters:
-
\f$
for inline math, as in\f$ E = m c^2 \f$
; -
\f[
and\f]
for unnumbered displayed equations, analogous to\begin{equation*}
and\end{equation*}
; and -
\f{environment}{
and\f}
for other LaTeX math environments, such aseqnarray
.
See this documentation for examples.
Including Code
There are a handful of different ways to include code blocks in Doxygen, but the one that seems to work in the widest variety of cases is the following:
/**
* Here's a bit of code
* \code{.cpp}
int
main(
int argc,
char* argv[])
{
std::cout << "Hello World!" << std::endl;
} // end of main()
\endcode
* that prints "Hello World!".
*/
The argument to the \code{}
command is a file extension that'll tell Doxygen what kind of syntax highlighting it should use. Unfortunately, between the \code{}
and \endcode
keywords, there is no stripping of leading asterisks as would normally take place. It will, however, strip out whatever leading indentation exists.
Trilinos' Policy
At a bare minimum, Trilinos requires that any new entities you add have a \brief
description, and functions additionally require at least the \param
s and \returns
information. It is the responsibility of the developer adding the new code to include the relevant documentation. It is the responsibility of the pull request reviewer to not allow a request to be merged until this minimum documentation is in place.
It is also highly recommended that any time you come across undocumented entities, that you either add the documentation yourself if you are knowledgable enough to do so, or contact the file author and ask them to add the documentation. Our goal is to have a fully documented code base, and the more proactive every developer is about that, the easier it will be to achieve.