Code Documentation & Comments - Looking Back

Thomas J Kennedy

Contents:

Most of your code in CS 150 and CS 250 probably had quite a few comments. Inline comments are not the focus of this discussion. The focus of this discussion is documentation of classes, functions, and methods.

1 A Few Starting Examples

I work in few different languages. Throughout my

You have definitely been told to “comment your code” in the past, but in a less formal fashion.

Let us start with a few selected documentation examples from my CS 330 and CS 417 notes.

1.1 C++

Doxygen can be used for C++. Consider the following Doxygen Example:

Example 1: C++ Doxygen Documentation
/**
 * Retrieve the value stored in three selected Cells
 *
 * @param cell1Id numeric id representing the 1st desired cell
 * @param cell2Id numeric id representing the 2nd desired cell
 * @param cell3Id numeric id representing the 3rd desired cell
 *
 * @return value stored in the Cell
 *
 * @pre (cell1Id > 0 && cell1Id < 10) &&
 *      (cell2Id > 0 && cell2Id < 10) &&
 *      (cell3Id > 0 && cell3Id < 10)
 */
CellTriple get3Cells(int cell1Id, int cell2Id, int cell3Id) const;

1.2 Java

Javadoc can be used for Java. Consider the following Javadoc Example:

Example 2: Javadoc Documentation
/**
 * Multi-thread Coin Flip.
 *
 * @param numTrials # flips to simulate
 * @param numThreads number of threads to use
 *
 * @return Completed FlipTasks
 *
 * @throws InterruptedException if a thread is stopped prematurely
 */
public static FlipTask[] multiThread(long numTrials, int numThreads)
    throws InterruptedException

1.3 Python

Pydoc or Sphinx can be used for Python. Consider the following Pydoc Example:

Example 3: Python 3 Pydoc Documentation
def parse_raw_temps(original_temps: TextIO,
                    step_size: int=30, units: bool=True) -> Iterator[Tuple[float, List[float]] ]:
    """
    Take an input file and time-step size and parse all core temps.

    :param original_temps: an input file
    :param step_size:      time-step in seconds
    :param units: True if the input file includes units and False if the file
                  includes only raw readings (no units)

    :yields: A tuple containing the next time step and a List containing _n_
             core temps as floating point values (where _n_ is the number of
             CPU cores)
    """

I prefer the Sphinx/Google style for Python.

Example 4: Python 3 Sphinx/Google Style Documentation
def parse_raw_temps(original_temps: TextIO,
                    step_size: int=30, units: bool=True) -> Iterator[Tuple[float, List[float]] ]:
    """
    Take an input file and time-step size and parse all core temps.

    Args:
        original_temps: an input file
        step_size: time-step in seconds
        units: True if the input file includes units and False if the file
            includes only raw readings (no units)

    Yields:
        A tuple containing the next time step and a List containing _n_
        core temps as floating point values (where _n_ is the number of
        CPU cores)
    """

1.4 Rust

Example 5: Rust Documentation
///
/// Take a room and change the flooring
///
/// # Arguments
///
///   * `original` - House to change
///
/// # Returns
///
/// House with the updated flooring
///
fn upgrade_flooring(original: &House) -> House {
    //...
}

Rust and Python have similar documentation styles (give or take some markdown formatting). Since we only cover small snippets of Rust in this course (for context), we will forgo a complete Rustdoc discussion.

2 Writing Good Documentation

All code should be properly and fully documented using a language appropriate comment style. All functions (including parameters and return types) must be documented.

2.1 Auditing the C++ Cookbook

You may recall my Cookbook Example from the Mutator-Accessor Unit Testing Notes from CS 350 (Introduction to Software Engineering) and (possibly) the C++ and Java class checklists in CS 330 (Object Oriented Programming and Design).

Let us start with a quick audit of a C++ Cookbook Example.

C++ Cookbook

The second constructor looks incomplete.

        /**
         * Create a Cookbook that can contain at most _r_
         * recipes.
         */
        Cookbook(int r);

Notice the int r? It is not documented. What does r represent? Let us start by adding @param r to the documentation

        /**
         * Create a Cookbook that can contain at most _r_
         * recipes.
         *
         * @param r max number of recipes that can be stored.
         */
        Cookbook(int r);

That is a little more clear. The description should probably be cleaned up a little.

        /**
         * Create a Cookbook with a set recipe limit.
         *
         * @param r max number of recipes that can be stored.
         */
        Cookbook(int r);

What did we do?

  1. We documented the input into the constructor (i.e., the parameter r) with an @param tag.
  2. We revised the description for brevity and clarity.

Any time a function has a parameter it should be documented. Should, not must. There are a few exceptions.

2.2 Auditing the Java Cookbook

Let us perform a quick audit of a Java Cookbook Example.

Java Cookbook

Similar to the C++ Cookbook… the second constructor looks incomplete.

        /**
         * Create a Cookbook that can contain at most _r_
         * recipes.
         */
        public Cookbook(int r);

The int r is not documented. What does r represent? Let us by add @param r to the documentation and clean up the description.

        /**
         * Create a Cookbook with a set recipe limit.
         *
         * @param r max number of recipes that can be stored.
         */
        public Cookbook(int r);

What did we do?

  1. We documented the input into the constructor (i.e., the parameter r) with an @param tag.
  2. We revised the description for brevity and clarity.

This feels like Déjà vu. We just updated the Java Constructor using the same process from the C++ version!

There are a few more tweaks to be made to the Java Cookbook documentation. However, I will leave those tweaks as a practice exercise.

2.3 Documentation for a New Function

Suppose we have just finished writing a quick program to simulate a trick coin (i.e., a coin where heads and tails are not equally probable).

bool one_flip(double p);

int main(int argc, char** argv)
{
    // Seed the random number generator with the current time
    srand(time(NULL));

    const int num_flips = 8;

    for (int i = 0; i < num_flips; i++) {
        if (one_flip(0.7)) {
            cout << "Heads" << "\n";
        }
        else {
            cout << "Tails" << "\n";
        }
    }

    return 0;
}

The one_flip function needs a description.

/**
 * Simulate a single coin flip.
 */

What does p represent? Does it represent the probability of heads or tails?

/**
 * Simulate a single coin flip.
 *
 * @param p probability of heads
 */

Now what about the return? We know that bool means a true or false. Which one do I get for heads? Let us add an @return.

/**
 * Simulate a single coin flip.
 *
 * @param p probability of heads
 *
 * @return true if the result is heads and false if the result is tails
 */
bool one_flip(double p);

There is no more ambiguity or guesswork. Both p and the possible return values are documented.

2.3.1 What about Java?

Java and C++ are similar. If we change bool to boolean, and rename one_flip to oneFlip… we have a Java function.

    /**
     * Simulate a single coin flip.
     *
     * @param p probability of heads
     *
     * @return true if the result is heads and false if the result is tails
     */
    boolean oneFlip(double p)
    {
        //...
    }
 

Notice how the documentation is identical? The basic Javadoc comment syntax overlaps Doxygen comment syntax.