Friday, December 12, 2008

C++ Coding Standard: Complete

Over the course of several weeks, I presented 12 sections of a C++ coding standard, along with some rationalizing verbiage. For your convenience, here is is in one shot, with no adornment.

The standard may be food for thought for you; or you might find it laughably pompous or incomplete. But I hope that you'll get your team to agree to some written standard, and use it to speed code reviews by letting reviewers make their points with little more than a chapter and verse -- something like "line 435 violates 3.8".
  • 1. Source files.
    • 1.1. Every class must have a header file called "{class}.hpp".
    • 1.2. Every class must have an implementation file called "{class}.cpp".
      • 1.2.1. Exception to 1.2: template class with only inline members.
      • 1.2.2. Exception to 1.1 and 1.2: class in anonymous namespace.
    • 1.3. {class}.hpp and {class}.cpp must be in the same directory.
    • 1.4. {class}.hpp must define one and only one class.
      • 1.4.1. Exception to 1.4: nested classes or structs.
    • 1.5. Filenames must match the class names exactly, including case.
  • 2. Header file layout.
    • 2.1. Header files must begin with the standard C++ file comment.
    • 2.2. Then comes the "#ifndef" of the include guard.
      • 2.2.1. Guard macro format _<DIR>_<CLASS>_H (all caps).
    • 2.3. Then include directives and external class declarations (optional).
    • 2.4. Then outside-the-class typedefs (optional).
    • 2.5. Then the class definition.
    • 2.6. Then other interface declarations (optional).
    • 2.7. Then inline function definitions (optional).
    • 2.8. Then the "#endif" of the include guard.
  • 3. Header file contents.
    • 3.1. A .hpp must never include an implementation file.
    • 3.2. A .hpp must include every file needed to compile itself.
    • 3.3. A .hpp must not include files not needed to compile itself.
    • 3.4. Class definitions must be preceded by a documentation comment.
    • 3.5. Function declarations must be preceded by a documentation comment.
      • 3.5.1. Document function pre- and postconditions when relevant.
    • 3.6. A .hpp must not declare variables or constants outside the class.
    • 3.7. A header file must not declare functions outside the class.
      • 3.7.1. Exception to 3.7: operators or template specializations may sometimes be declared outside the class.
    • 3.8. A header file must not use "using namespace" directives.
    • 3.9. Any functions declared in the .hpp which are to be inline, must be defined in the .hpp.
  • 4. Implementation file layout has only a few restrictions.
    • 4.1. Implementation files must begin with the standard C++ file comment.
    • 4.2. The first non-comment line of {class}.cpp must be: #include "{class}.hpp".
    • 4.3. {class}.cpp definitions should be in the same order as {class}.hpp declarations.
  • 5. Implementation file (.cpp) contents.
    • 5.1. A .cpp must never include an implementation file.
    • 5.2. A .cpp must not include files not needed to compile itself.
    • 5.3. Every non-inline function declared in {class}.hpp must be defined in {class}.cpp.
      • 5.3.1. Exception to 5.3: copy constructor and assignment operator may be declared private and not implemented, to override C++ defaults.
    • 5.4. Every static member variable declared in {class}.hpp must be defined in {class}.cpp.
    • 5.5. Anything defined in {class}.cpp which is not declared in {class}.hpp must be in the anonymous namespace.
    • 5.6. Every anonymous namespace function must be preceded by a documentation comment.
  • 6. Class design.
    • 6.1. Non-static data members must be private.
    • 6.2. A class must implement at least one public constructor.
    • 6.3. A class must declare its copy constructor and assignment operator.
      • 6.3.1. Exception to 6.3: not if declared private in base class.
    • 6.4. Member functions which do not change data members must be declared "const".
    • 6.5. Protected members should not be used.
    • 6.6. Consider changing private member functions to anonymous namespace functions in the implementation file.
    • 6.7. Classes must not declare "friend" classes.
    • 6.8. Classes should not declare "friend" functions for non-inline functions.
    • 6.9. Declare blank constructor if and only if a client needs it.
    • 6.10. Consider calculating data instead of storing it.
    • 6.11. Data members allocated on the heap must have type auto_ptr<T> or boost::shared_ptr<T>.
    • 6.12. Member functions marked "const" must be idempotent.
  • 7. Class layout.
    • 7.1. All public things first, then all protected, then all private.
    • 7.2. Within a public or private section, use the following order:
      • 7.2.1. First any nested type definitions or typedefs.
      • 7.2.2. Then constructors, destructors, and the assignment operator.
      • 7.2.3. Then any const member functions.
      • 7.2.4. Then any non-const member functions.
      • 7.2.5. Then any static member functions.
      • 7.2.6. Then any data members.
    • 7.3. Define inline functions outside of the class definition.
  • 8. Inheritance.
    • 8.1. Public inheritance must only be used to model the "is a" relation.
    • 8.2. Use private and protected inheritance sparingly, and only to model the "looks like a" relation.
    • 8.3. Only inherit publicly from an abstract base class.
    • 8.4. Destructors of public base classes must be pure virtual (but implemented).
    • 8.5. Don't use multiple inheritance.
  • 9. Preprocessor issues.
    • 9.1. Preprocessor macros should be rarely used.
      • 9.1.1. Obvious exception to 9.1: multiple-include guard.
      • 9.1.2. Use enum constants instead of constant macros.
      • 9.1.3. Use inline functions instead of function macros (when possible).
    • 9.2. Never use a macro to invent new syntax.
    • 9.3. Conditional compilation should be rarely used.
    • 9.4. Operating system #includes use angle brackets, e.g. <unistd.h>.
    • 9.5. Non-operating system #includes use double quotes, e.g. "Object.hpp".
    • 9.6. Use the standard C++ system header files, e.g. <string> not <string.h>.
  • 10. Naming conventions.
    • 10.1. Constant names must be in all caps.
    • 10.2. Private data and function member names must begin with '_'.
    • 10.3. Type names must begin with a capital letter.
    • 10.4. Function macro names must be in all caps.
    • 10.5. Names not covered above must begin with a lower-case letter.
    • 10.6. Do not use underscores and mixed-case in a single name.
    • 10.7. Typedefs must not obscure the fact that a type is a pointer, reference, or array type.
    • 10.8. All class definitions must be within a namespace.
  • 11. Function design.
    • 11.1. Constructors must initialize all data members in the initialization list, and in the order they're declared in the class definition.
    • 11.2. Class member functions which return a pointer or reference to a data member, must return it as "const T*" or "const T&".
    • 11.3. A class function must not deallocate memory that was allocated outside of the class implementation file.
    • 11.4. A class destructor must deallocate all of the memory allocated for data members of the instance.
    • 11.5. Function arguments which are pointer, array, or reference types, which are not changed by the function, must be declared "const".
    • 11.6. A function must contain at most one "return" statement.
    • 11.7. Do not use "goto" or "continue".
    • 11.8. Only use "break" in switch statements.
    • 11.9. Declare variables at the latest possible location.
    • 11.10. Use assert() to check for programming or interface errors.
    • 11.11. Functions with "true/false" semantics must return "bool".
    • 11.12. Declare default arguments in the .hpp file, not the .cpp file.
    • 11.13. Pass objects (especially containers) by (const) reference, not value.
    • 11.14. Return objects (especially containers) by value, unless it makes sense to return a const reference.
    • 11.15. Exceptions must be caught by reference, not by value or pointer.
    • 11.16. When re-throwing an exception, use "throw;", not "throw ex;".
    • 11.17. Destructors must catch all exceptions and throw none.
    • 11.18. Exception specifications must not be used.
      • 11.18.1. Exception to 11.18: a destructor which catches all exceptions can be marked with "throw()".
  • 12. Other stylistic issues.
    • 12.1. Mark 64-bit constants "LL" or "ULL", e.g. 0xffffffffffffffffULL.
    • 12.2. Mark 32-bit constants >= 0x80000000 "U", e.g. 0xffffffffU.
    • 12.3. Use C++-style typecasts instead of C-style typecasts.
    • 12.4. A source file must not include tab characters (only spaces).
    • 12.5. Lines must contain no more than 80 characters, including the (invisible) newline character at the end.
    • 12.6. A source line must not contain two statements.
    • 12.7. Use 2 spaces per level of indentation.
    • 12.8. Indent the body of a compound statement ("if", "while", etc.) one level more than the head of the statement.
    • 12.9. An "else" must have the same indentation as its "if".
    • 12.10. Two statements in a sequence must have the same indentation.
    • 12.11. A comment is indented the same as the statement following it.
    • 12.12. No character must appear to the right of a brace ("{" or "}").
    • 12.13. A closing right brace must be indented to the same level as the line containing its corresponding left brace.
    • 12.14. There must be no space between the function name and the left parenthesis of a function call (e.g. "printf(...)").
    • 12.15. There must be a space between a keyword and a left parenthesis (e.g. "if (x != NULL)").
    • 12.16. Do not use C-style comments (except for documentation comments).
    • 12.17. Do not comment out or "#if 0" dead code. Remove it.
    • 12.18. Do not use external include guards.

No comments: