6 tips for effective C++ documentation

bartEmbedded DevLeave a Comment

Software code documentation is not just about mentioning “I have a variable and it is named X”. Code documentation is about explaining what the code is mend to do and how it is to be used. In this article for each at least three questions will be stated that will help you to write better C++  documentation. The following code elements will be addressed:

  1. File headers
  2. Class and structs
  3. Functions
  4. Variables
  5. Enumerations
  6. Const expressions

The code examples given make use of Doxygen formatting. Of course you are free to use your own formatting style but as we use Doxygen at Embedded AMS. Lets get started!

1. File headers

Our style is minimal towards file headers. Where as you often see the creation date and authors in the header we prefer to leave those out. One edit later by a different colleague and the header is out dated. If we need this information we look in our version control system.

For legal reasons we do include the copyright notice. This is especially a good idea when creating code which is visible by others outside of your organisation. For instance when creating a closed source library with public headers or an open source project with limited liability.

/* Copyright (C) Embedded AMS 2018 - All Rights Reserved
 * Unauthorised copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential. Modification of this file and the content
 * herein in any form is also strictly prohibited unless written consent has
 * been given.
 */

2. Classes and Structs

Classes are of course the backbone of C++, it is thus very important to make it very clear where and how to use a given class. Remember, to you as the programmer and designer of a class its purpose is clear. A colleague might not have the same insight. Answering the following questions in the class documentation section at the start of the class will help them a lot in understanding the context: 

  1. Where is this class used?
  2. What are the functions you should use to interact with this class?
  3. How do you initialize this class.
  4. Is there an order to how functions should be called.
  5. How does this class function internally.
  6. Should or should you not inherit from this class?

Just as with images a code example states a 1000 words. Listing an example in the class documentation on how to uses it is never a bad idea.

//! This class is the base class for any type of engine, such as petrol, diesel, gas turbine and electric.
/*! 
    Place the answers to the questions here.

    Optionally add the code example here.
*/ 
class engine {
    // Class content.
};

3. Functions

3.1 General functions

Documentation for any function consist out of three blocks:

  1. What does this function do,
  2. Which input parameters are require, and
  3. What is returned.

So first and foremost it is important to state the purpose of a function especially if it has effects not directly visible in its return value. This does not mean that you need to state the obvious for every getter and setter function. If the state of a class is changed by a function mention this.

When documenting function parameters and return values of plain basic types always state the unit of the variables used. For instance one of the parameters to a function is speed. Please specify what the functions expects, for example: m/s, km/h, f/s, miles/h, etc. A better way to do this to use predefined unit types (speed) and use literals to convert plain values to this unit. This topic has been covert in THIS article.

When programming C++ it is uncommon to have functions outside of a class. If you have a really good reason to have this function, let the world know! State this function is written here because…

3.2 Class functions

For any class function that is to be used by others it should be clear where, when and how. For internal helper functions we can a bit less strict. Whether a function is used out side the class is of course clear if it is private or public. One has to take into account that functions with a public access require explanation without knowledge of the class internals. In other words when you document a private or protected function you can assume that the reader has a better understanding of the inner workings of the class. For public functions you can only assume that the reader has read the general documentation of the class.

3.3 Virtual class functions

For virtual functions which the user is able to override it should be clear what is expected of the derived class. The goal of this function should be very clear to the programmer writing the inherited class. Which internal class variables should he/she modify. Are there any helper functions which aid the programmer in reaching this goal?

4. Variables

Three things are of importance for variables:

  1. What is the unit or type of this variable,
  2. Where are they modified,
  3. Is this variable an intermediate result or final value.

5. Enumerations

When documenting enumerations there are two points to take into account:

  1. The enumeration as a whole, and
  2. The individual items.

In the following example we have a simple enumerations detailing different states for a state machine. As can be seen the documentation states where the enum is used and why the size is specified. For each state it’s purpose and transitions are stated.

//! This enumeration of states is used by state machine X.
/*!
  Because this state is communicated it's size limited to 8 bit.
*/
enum class States : uint8_t {
  IDLE,  //!< The initial state in which we wait for the command to initialize. 
  INIT,  //!< This sets all hardware configurations. Once done we directly continue to RUN.
  RUN,   //!< This is the operational state. Only a stop command will exit the state.
  STOP   //!< Disable the hardware, once done enter idle.
}

6. Const expressions

The constexpr introduced in C++ 11 can have many usages. From defining constants to implementing simple functions. Depending on the case the suggestion is to tread the const expression as either a normal variable or function.

Final remarks

Where to place the documentation, in the header or source file? For any library always use the header. This way you can make the library closed source while still releasing the headers with documentation. The headers you have to release anyway for the user to be able to compile against your lib.

Leave a Reply