PSCF v1.1
Coding Standards

Build System (Prev)         Parameter File I/O (Next)

This file documents programming and formatting conventions that should be used in the source code of PSCF:

File Names

  • Header files that may be included in other files use file extension *.h
  • Compilable C++ source files use extension *.cpp
  • Files that contain definitions of member functions for class templates are sometimes separated from the header file and given names that end in an extension *.tpp
  • One class per file: Avoid including definitions or definitions involving more than one public class in a single file.
  • Use the same base name for e header and implementation for the same class: The header and source files for a class named MyClass should be MyClass.h and MyClass.cpp. If MyClass is class template, some of the implementatiom may be defined in a file MyClass.tpp.
  • Place the header and implementation files for each class or class template in the same directory within the src/ directory tree.
  • Use header guards in all header files.

Symbol Names

  • Names of functions and variables are lower case camel, as in "myVariable" or "myData".
  • Names of user-defined types (class, typedef, and enum) and namespaces are upper case camel, as in "MyClass" or "Util".
  • Names of private or protected class member variables must end with with a trailing underscore, like "data_" or "outputFile_".
  • Public static constant class member variable names are upper case camel, as in "MaxDimension".
  • Use plural nouns for names of arrays and other containers. A private array of Monomer objects might thus be named "monomers_".
  • Names of pointer variables end with a suffix Ptr. A local Thing* pointer variable within a function might thus be called "thingPtr". A Thing* pointer that is a private or protected class member variable might be called "thinkPtr_". Do not use this convention for pointers that point at C arrays (and avoid bare C-arrays): The suffix "Ptr" denotes a pointer that will point at a single object.
  • Names of preprocessor macros are upper case, with underscores between words.
  • Use the same parameter names in the declaration of a function and in the corresponding definition.
  • Names of functions that are not simple accessors (discussed below) should usually be verbs.
  • The name of a "setter" class member function that is passed a value for a non-public member variable should begin with the prefix "set".
    The name of the function parameter that holds the new value should be the same as the name of the non-public member variable, without any trailing underscore. The same convention is used whether the value is passed by value or by reference. For example:
    void Thing::setData(int data)
    { data_ = data; }
    void Thing::setPosition(const Vector &position)
    { position_ = position; }
    void Thing::setMolecule(Molecule &molecule)
    { moleculePtr_ = &molecule; }
  • The name of a simple accessor ("getter") class member function that returns a non-public member variable by value or by reference should simply be the name of the member variable, without any trailing underscore. PSCF thus does NOT use the convention of appending a prefix "get" to the names of accessor functions. The same name convention is use for accessors that return by value, const reference, or non-const reference. Thus, for example:
    int Thing::data() const
    { return data_; }
    const Vector& Thing::position() const
    { return position_; }
    Vector& Thing::position()
    { return position_; }
    Molecule& Thing::molecule()
    { return *moleculePtr_; }
  • The name of the preprocessor macro used in a header guard should be the name of the namespace followed by the file name, in upper case with underscores between words. A header file named "SillyClass.h" that contains a class Util::SillyClass in namespace Util should be enclosed with header guards like this:
    #ifndef UTIL_SILLY_CLASS_H
    #define UTIL_SILLY_CLASS_H
    
    namespace Util
    {
    
       class SillyClass : public Base
       {
          \\ ....
       }
    
    }
    #endif
    

Code Formatting:

  • Indent exactly 3 spaces per level. Do NOT use any tabs (ever).
  • For control structures (if, for, while, etc.), place the opening brace at the end of a line, and the closing brace on a line by itself, aligned with the beginning of the opening line, like this:
    for (int i = 0; i < end; ++i) {
    doSomething();
    }
  • For functions with more than one line, put the opening brace on a separate line, and align opening and closing braces, like this:
    int SillyClass::sillyMethod(int param1, int max)
    {
    for (int i = 0; i < max; ++i) {
    param1++;
    }
    if (param1 > 0) {
    return 0;
    } else {
    return 1;
    }
    }
  • For one-line functions, the function definition may be given on a single line, like this:
    inline int SillyClass::data()
    { return data_; }
  • Set off the operators =, ==, <, >, +, and - by one space on either side, with occasional exceptions. Multipication (*) and division (/) operators may or may not be set off by white space. Make some exceptions to avoid line wraps.
  • Use one space to separate keywords, parentheseses, and opening braces in conditional statements. Use one space after each semi-colon in for-loop statements, and one space after each comma in function parameter lists.
  • Do not follow opening parentheses or precede closing parentheses by a space. Do not add whitespace space before commas or semicolons.
  • Consecutive function declarations or definitions within a file, along with associated documentation blocks, should be separated by a single blank line.
  • Break lines less than 80 characters per line whenever possible, to preserve readability in printouts and small laptop terminal screens.
  • Wrap every source file in the src/ directory in a namespace block. Start the namespace declaration in the first column.
  • In class definitions, align "public:", "protected:", and "private:" declarations with the beginning of the class definition statement, and with the closing brace. List public members first, then protected, then private. Within each block, list member variables first, then member functions
  • List any friend declarations at the end of a class definition in a "pseudo-block" that is preceded by a comment "//friends:" on a line by itself, after the private members. The "//friends:" comment should be aligned with "public:" and "private:" declarations.
  • Inline method definitions should usually be given outside the class definition, within the header file. The word "inline" should be added to this function definition, but not the function declaration.
  • Example (with doxygen documentation):
    #ifndef UTIL_SILLY_CLASS_H
    #define UTIL_SILLY_CLASS_H
    
    namespace Util
    {
    
       /**
       * A truly pointless class.
       */
       class SillyClass : public Base
       {
    
       public:
    
          /**
          * The first method.
          *
          * \param param1 a globble
          * \param param2 a gloob
          */
          int method1(int param1, double param2);
    
          /**
          * Get buddy (by reference)
          */
          const Buddy& buddy() const;
    
       protected:
    
          /**
          * Get buddy  by non const reference)
          */
          Buddy& buddy();
    
       private:
    
          int      data1_;
          Buddy* buddyPtr_;
    
       //friends:
    
          friend class Buddy;
    
       };
    
       // Inline methods
    
       inline const Buddy& SillyClass::buddy() const
       {  return *buddyPtr_; }
    
       inline Buddy& SillyClass::buddy()
       {  return *buddyPtr_; }
    
    }
    #endif
    

Documentation (via doxygen):

PSCF uses the doxygen (www.doxygen.org) utility to create html documentation from documentations blocks that are extracted from the source code. Doxygen will extracts any multi-line comment that begin with a slash and two asterisks ("/**"), or single line comments that begin with three slashes ("///"). See comments in the following example:

   /**
   * This comment will be extracted by doxygen (note the extra asterisk)
   */

   /// So is this one (note the extra slash)

   /*
   * This comment, however, will not be extracted by doxygen.
   */

   // Nor will this one.

Comments within functions that you do not wish to be extracted by doxygen should use the usual form for C comments, using only a single asterisk for multi-line comments or only two slashes for for single line comments, as indicated in the above example.

  • Create dOxygen documentation blocks for public and protected named quantities, i.e., all classes, public and protected member functions, protected member variables, namespaces, global functions, typedefs, enums, and constants.
  • The doxygen documentation block for a class should appear immediately above the first line of the class definition.
  • The doxygen documentation block for a class member function should be immediately above the function declaration, within the class definition, in the header file.
  • Prefer doxygen multiline comments for documentation of classes and class member functions.
  • Document all parameters of public and protected functions, using the dOxygen param keyword.
  • The documentation for every class and public or protected function should begin with a brief single-sentence description, which must end with a period and be followed by a blank line. The brief description should usually not extend beyond one line. This brief description is often sufficient. If needed, more detailed discussion may be given in one or more subsequent paragraphs, separated by blank lines.

Example:

   /**
   * Align the universe.
   *
   * This is a longer discussion of what the method does, and of how and
   * why it does it. It may also contain a discussion of assumptions, and
   * of the algorithm.
   *
   * A longer discussion may contain two or more paragraphs, separated by
   * blank lines.
   *
   * \param thing1 value of some quantity
   * \param thing2 flag to determine what to do with thing1
   * \return shift in the position of the universe
   */
   double alignUniverse(double thing1, bool thing2);
  • Document private class member variables using either the multi-line or single line doxygen format. Documemtation of private members does not normally appear in the html documentation, but can be included if desired. Documentation of the meaning of member variables is important for developers who must read the implementation, though not strictly necessary for users.
  • Do not use doxygen-style comments for the function definitions that are given outside the class definition. Use a conventional C/C++ comment format (with one asterisk or two slashes) above each such function definition, like this:
       /*
       * Brief reminder of purpose. Possibly comments on implementation.
       */
       void MyClass:myFunction(int param)
       {
           // Do something here
       }

Interface Design

  • Make all nonstatic class member variables private or (less frequently) protected.
  • Pass and return primitive C/C++ data types by value. Pass primitive data types to functions by non-const reference only when they must be modified within the function.
  • Pass objects (class instances) to functions by reference, not by value. Pass by const reference if the object is not modified.
  • Prefer references over pointers as function parameters. Pass pointers to functions only if: i) a null value for the pointer is a meaningful possibility, or ii) the pointer contains an address that must be re-assigned within the function.
  • Prefer references over pointers as function return values. Return a pointer only if a null pointer is a meaningful possibility.
  • Practice strict "const correctness". Mark function parameters and class member functions as const whenever possible.
  • Read-only access to a member variable of a primitive C/C++ type should be provided (when needed) by an accessor function that returns the member variable by value. Read-only access to an object (class instance) that is owned by a class may be provided by an accessor function that returns the object by const reference. Read-write access to an object may be provided by an accessor that returns the object by non-const reference. Simple accessors that return by value or const references should be declared as const functions. For example, if a class has an int member variable data_ and a member object_ that is an instance of class Object, you might consider providing any or (none) of the following methods:
    int Thing::data() const
    { return data_; }
    const Object& Thing::object() const
    { return object_; }
    Object& Thing::&object_()
    { return object_; }
  • Providing an accessor function that returns a non-const reference to a member object is equivalent to making the member public, and should be used when this is the desired behavior. Do not instead simply make the data member a public member.

Accessors that return non-const references provide pseudo-public access to data members (i.e., variables or objects). The advantages of this scheme over simply making selected data members public are:

  • Uniform interface: All members must be accessed through accessors, so users need not remember which members are public and which are accessed through accessor functions.
  • Uniform name conventions: The name of the accessor that returns a member is always the name of the member variable, without amy underscore. This allows us to use an underscore to mark member variable names, without exposing underscored names outside the class implementation.
  • Uniform access: The same name convention is used for accessors that return by value, const reference, or reference, but the compiler can still enforce access control.
  • Implementation hiding: The same convention is for functions that access members of a class and those that access objects athrough a pointer. Use of accessor functions also makes it possible to add and remove sanity checks for debugging, such as checks that pointers are not null.
  • Write access to a class instance may sometimes instead be provided by an explicit "set" function. The use of a set function is preferable when one needs to check preconditions or carry out operations necessary to guarantee data validity.

Data Structures

  • Use the C++ iostream classes for all file IO. For consistency, avoid the C fscan() and fprint() methods. Use the wrapper classes in the src/util/format directory to simplify coding of formatted output to ostreams.
  • Use std::string to represent character strings whenever possible.
  • Prefer the array container templates in src/util/containers over bare C arrays and STL containers. STL containers such as std::vector should be used when there is not an equivalent home-brewed container. Our preference for the home-brewed containers is based on the fact that:
    • Our home-brewed containers provide optional bounds checking, which is enabled only when NDEBUG is not defined.
    • Unlike std::vector, the most commonly used home-brewed array template DArray is not automatically resizable, and so will never be silently moved to a new location in memory.

Header File Includes

  • In header files, prefer forward class declarations over inclusion of class headers when possible. Forward declarations are sufficient for classes or class templates that used in the header file only in function declarations (as parameter or return types) or in the declaration of types for pointer member variables. Header files inclusion is generally necessary for base classes, member objects, and classes that are used in the implementation of inline functions.
  • Header files that provide information required in another C++ file should normally be included explicitly, even if it is known that they would be indirectly included via another included header file. Reliance on indirect inclusion is fragile, and explicit inclusion helps document the dependency. Exceptions are:
    • A class implementation file MyClass.cpp may rely on indirect inclusion of header files that are included by the corresponding header file MyClass.h
    • A derived class may sometimes rely on indirect inclusion of header files that are included by a base class.
    • The Exception.h header is should always be included indirectly by inclusion of src/util/global.h.
  • Use explicitly qualified names for standard library symbols such as std::string in function parameter lists and return values. Use this convention both in function declarations and functions definitions.
  • Never use a "using std" statement to load the entire C++ standard library namespace into a header file.

Error Handling

  • Include the file <util/global.h> in all files that use C assert() statements or that can throw exceptions. This automatically includes the "Exception.h" and "Log.h" header files, defines the UTIL_THROW(..) macro that is used to throw exceptions, and enables assert() statements if UTIL_DEBUG is defined,
  • For checks that are intended only for debugging, use C assert() statements or enclose the test in a ifndef UTIL_DEBUG .... endif conditional compilation block.
  • Use the UTIL_THROW macro defined in src/util/global.h to throw Exception objects for all errors, except those caught in debugging mode by C assert() statements. Like the standard assert() macro, this macro prints a message and showing the file and line number from which an Exception is thrown, as an aid to debugging.
  • Do not Exceptions for control flow. Exceptions should be used only for errors, and should normally cause the program to terminate.


Build System (Prev)         Developer Information (Up)         Parameter File I/O (Next)