Coding Standards

The purpose of a program is to tell another person what you want the machine to do. - Donald Knuth

This document outlines standards for coding in C++. Small modifications may also be used as a coding standard in Java. These standards fall into several categories:

  1. File Structure
  2. Formatting Conventions
  3. Naming Conventions
  4. Scoping Conventions
  5. Usage Conventions
  6. Compile Errors and Warnings

1. File Structure

  1. The file should conform to ISO standards for text files. In particular:
    1. The Windows carriage return character ^M should not appear in the file
    2. The file should end with a newline '\n'
  2. No tabs should be in the file
  3. No line should contain more than 80 characters
  4. The elements of a code file should appear in this order:
     
    file header documentation
    #include statements
    constants
    class declarations (if needed)
    function prototypes (if needed)
    class definitions (if needed)
    implementations
     
  5. Files for code definitions and implementations should adhere to these customs:
    1. For functions: prototypes should be placed in a file with suffix ".h" (often called a header file), and implementations in a corresponding file with suffix ".cpp" (often called an implementation file):
      xxx.h   # contains function prototypes
      xxx.cpp # contains implementations of functions prototyped in xxx.h
      
    2. For classes: class definitions should be placed in a file with suffix ".h" (often called a header file), and class member function implementations in a corresponding file with suffix ".cpp" (often called an implementation file):
      xxx.h   # contains class definition and related function prototypes
      xxx.cpp # contains implementations of functions and methods prototyped in xxx.h
      
    3. For templates (either function or class): class definitions and all implementations should be placed in a header file with suffix ".h":
      xxx.h   # contains class and function definitions and implementations
      
    4. A single header file may use the #include directive to make other physical files logically part of the header file.

2. Formatting Conventions

  1. Indentation should be set to two spaces
  2. Open brace { should appear on a line by itself, unless its matching close brace is on the same line. (Handle other delimiter pairs the same way.)
  3. Close brace }, possibly followed by a semicolon };, should appear on a line by itself, unless its matching open brace is on the same line. (Handle other delimiter pairs the same way.)
  4. Open and close delimiter pairs (braces {}, parentheses (), angle brackets <>) should be aligned vertically or horizontally:
    void MyFunction { /* one line of code here */ }  // OK
    void MyFunction                                  // OK
    {
      // code here
    }
    void MyFunction {                                // NOT OK
      // code
    }
    
  5. Function headers may be broken into "parameter documentation" format:
    void MyBigFunction
      (
        type1 parameter1, // documentation for parameter1
        type2 parameter2, // documentation for parameter2
        type3 parameter3, // documentation for parameter3
        type4 parameter4  // documentation for parameter4
      )
    {
      ...
    }
    
    Note that the matching open and close parentheses are vertically aligned and indented two spaces. This format is required for headers longer than 80 characters.
  6. Binary operators should be surrounded by clear space, except possibly multiplication in arithmetic expressions:
    x = a + b * c;   // OK
    x = a + b*c;     // OK
    x = a+b*c;       // NOT OK
    x=a+b*c;         // NOT OK
    x= a + b * c;    // NOT OK
    std::cout<<x;    // NOT OK
    std::cout << x;  // OK
    std::cin >>y;    // NOT OK
    
  7. When declaring pointers, leave space between * and the variable:
    int* iptr;     // OK
    int* iptr1,    // OK
       * iptr2;    // OK
    
    int * iptr;    // OK
    int * iptr1,   // OK
        * iptr2;   // OK
    
    int *iptr;     // NOT OK
    int *iptr1,    // NOT OK
        *iptr2;    // NOT OK
    

3. Naming Conventions

  1. Compound names should use upper case letters to mark the beginning of the next word likeThis and LikeThisToo
  2. Names of user-defined types (classes or enumerated types) should begin with upper case letters LikeThis or This
  3. Names of functions, including class methods, should begin with upper case letters LikeThis or This
  4. Names of variables should begin with a lower case letter likeThis or this
  5. Names of class variables should end with the underscore character likeThis_ or this_
  6. Names of types and functions should be chosen to be self-documenting
  7. Names of meaningful variables should be chosen to be self-documenting. Names of variables whose function is internally important only, such as loop counters, should be simple. The following code for function Average illustrates these points:
    float Average (int* a, size_t size)
    {
      if (size == 0) return 0;
      float sum = 0;
      for (size_t i = 0; i < size; ++i)
      {
        sum += a[i];
      }
      return sum / size;
    }
    
    Note that a is an array of integers (by declaration), size is the size of the array, sum represents the sum of array elements, and i is a loop counter. Both size and sum have meaning beyond their type declaration and this meaning is captured in the choices of names. On the other hand, a and i have no meaning beyond what is given in their declarations.

4. Scoping Conventions

  1. Resolve namespace scopes explicitly. Do not use general scope breakers such as C++ using directives. For example:
    using namespace std;          // not OK
    ...                           // not OK
    cout << "Hello World\n";      // not OK
    
    std::cout << "Hello World\n"; // OK
    
    The reason: The "using" directive effectively brings all of the namespace into the global namespace, thus circumventing the reason we have namespaces in the first place, which is to compartmentalize code. It is much clearer code to resolve the scope of an object where and when it is needed.
  2. Distinguish between meaningful variables and meaningless variables. For example, in the function Average above, size and sum are meaningful, whereas a and i are meaningless (except for type).
  3. Meaningless variables such as loop counters should be given as small a scope as possible by declaring them at the point of first use. In particular, loop counters should have scope limited to the loop itself, as illustrated above.
  4. Meaningful variables should be declared at the beginning of the block within which they are used, rather than at the point of first use. This convention serves to clarify the meaningful computational elements in a block at the beginning, which makes the code organization easier to understand.

5. Usage Conventions

  1. Every code file should have file header documentation containing at least the following information: name of the file, date created, author/coder, and a brief description of the file contents. A copyright statement may be used as well, typically the last item in the header documentation. Here is one style for header documentation:
    /*
        myfile.h
        Chris Lacher
        12/31/2008
        version 2.0
    
        Illustrates code file header documentation
    */
    
    The following version applies tags for automated documentation systems:
    /**
        @file    myfile.h
        @author  Chris Lacher
        @date    2008-12-31
        @version 2.0
    
        Illustrates code file header documentation
    */
    
    
  2. Most class definitions are documented in the file header. However, in situations where more than one class is defined in a file, each class should have basic documentation sufficient to clarify the intended use of the class. This may be part of the file header documentation or separate documentation for each class in the file.
  3. To the extent that functions are not self-documenting, they too should have documentation explaining the intended use and functionality captured by the function.
  4. Code detail documentation should be used sparingly. Generally, code should be written to be self-documenting and formatted to be readable, so that no code-level documentation is needed. In the rare cases where the code is so intricate that it is difficult to follow it may be appropriate to supply the reader with a summary of how the computation is being made. However, be wary to keep the documentation up to date. It is quite common for documentation to assert something is done while the code does something else entirely. Needless to say, this is less helpful than simply having no documentation.
  5. Always protect header files from multiple read using the
    #ifndef _FILENAME_H
    #define _FILENAME_H
    
    // code here
    
    #endif
    
    protocol.
  6. Always use angle brackets for include files:
    #include <myfile.h>             // OK - location of file is unspecified
    #include "myfile.h"             // NOT OK - location of file is hard coded (relative)
    #include "/directory/myfile.h"  // NOT OK - location of file is hard coded (absolute)
    
    The reason: angle brackets allow for the included file to be movable without editing the file in which they are included. Quotes force an edit of #include statement whenever the relative locations of the includee and includor are changed or the absolute path of the includee is changed. It is much better to resolve these issues in the build record (makefile).
  7. A close brace that is separated from its matching open brace by more than 30 lines should be documented:
    namespace xyz
    {
      ...
      switch(x)
      {
        ...
      } // switch(x)
      ...
    } // namespace xyz
    
  8. Use prefix increment and decrement operators unless the postfix return value is required:
    x = y++; // OK - x has the value of y before y is incremented
    x = ++y; // OK - x has the value of y after y is incremented
    ++y;     // OK - no return value is created
    y++;     // NOT OK - return value is created and wasted
    
  9. Use declared constants rather than defined constants:
    #define PI 3.14159          // NOT OK
    const double PI = 3.14159;  // OK
    
  10. Literals should not be embedded in code. Use constants to capture specific numerical, character, and string literals.
  11. Every (non-static) class variable must be initialized in an initialization list in every class constructor. This applies even to variables that are re-initialized in the constructor body. For example:
    class IntArray
    {
    public:
      IntArray ();                // default constructor
      IntArray (size_t);          // 1-parameter constructor
      IntArray (const IntArray&); // copy constructor
      ...
    private:
      size_t size_;
      int *  data_;
    };
    
    // default constructor
    IntArray::IntArray () : size_(0), data_(0)
    {}
    
    // 1-parameter constructor
    IntArray::IntArray (size_t size) : size_(size), data_(0)
    {
      data_ = new int [size_];
    }
    
    // copy constructor
    IntArray::IntArray (IntArray& ia) : size_(ia.size_), data_(0)
    {
      data_ = new int [size_];
      for (size_t i = 0; i < size_; ++i)
        data_[i] = ia.data_[i];
    }
    

6. Compile Errors and Warnings

  1. Code should compile without errors on Linux/Intel [linprog machines].
  2. With all warning flags set, code should compile without issued warnings on Linux/Intel architecture [linprog machines].
  3. The alternative Unix/Sun architecture is a second and desireable place to test code.