C++ Coding Standard
Scope and Purpose
The intent of this standard is to aid C++ software developers in general rules of style and practices concerning design and coding. These rules provide guidance in the development of more reliable, testable, portable, maintainable, efficient, and technically correct source code. This standard also encourages what are widely considered by commercial industry as good coding practices.
Conventions
As the rules are listed, please note the use of the words shall and should:
- When a standard includes the word shall, the standard must be followed unless a deviation against the software project plan has been received to diverge from it. The divergence from the standard MUST be documented in the source code with appropriate comments.
- When a guideline includes the word should, the guideline is intended to be followed under normal circumstances. If the developer feels that the guideline is not applicable, he/she is free to diverge from it without securing permission from the task lead. The divergence from the guideline should be documented, though, to draw attention to the situation.
Comments
- Doxygen formatting shall be used for ALL header files. A reference on Doxygen is available here.
- "Line comments" - Comments placed on lines separate from the code they describe:
- Line comments shall be separated from previous code by one blank line.
- The commented token (//) shall be aligned with the code being supported.
- There shall be one space between the comment token and comment text.
// Output the value of every element in the vector for(iter = pVec->begin(); iter != pVec->end(); iter++) { os << "Value: " << *iter << std::endl; } // Increment the value of every element in the vector for(iter = pVec->begin(); iter != pVec->end(); iter++) { (*iter)++; }
- "Inline comments" - Very short comments placed on the same line as the code they describe:
- Inline comments shall be spaced to separate them from code.
- Multiple instance of inline comments shall be aligned where possible.
- Inline comments shall not duplicate code.
- Inline comments shall only be used if they are needed for clarity.
struct example_struct_t // An example struct { void* pBuffer; // Pointer to generic buffer in memory long iBufferLen; // Length of generic buffer in memory };
- "Block commenting" (/* and */) shall not be used, except in C-language modules, where it is necessary.
/* Single line sample comment */ /* Multiple line sample comment */
- The closing brace of long code blocks shall be commented
for(;;) { ... } // End for().
- #endif statements shall be commented to denote what preprocessor conditional is being ended.
#ifdef __WIN32 ... #endif // __WIN32
- Comments shall be used throughout the code to provide documentation and clarification of the logic, data, and algorithms.
- Implementation files shall be commented as to provide information to those people who have to MAINTAIN the classes.
- Include files shall be commented as to provide information to those people who have to USE the classes.
- You MUST NOT remove any of the people named in the copyright list. This is part of the license agreement block.
- You MUST add your copyright, name and e-mail address to any file to which you have made a significant contribution. Use your judgement here, but if you simply change something like whitespace or reformat the code, that is not significant.
- Every file shall describe its contents and contain a copyright notice. The following comment block should be used as a minimum:
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ // Zen <PROJECT OR SUBPROJECT NAME> // // Copyright (C) 2001 - 2009 Tony Richards // Copyright (C) 2008 - 2009 Matthew Alan Gray // . // . // . // Copyright (C) 2008 <AUTHORNAME> // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // /// @brief <SHORT DESCRIPTION> /// /// <LONG DESCRIPTION> /// /// /// @author Tony Richards trichards@indiezen.com /// @author Matthew Alan Gray mgray@indiezen.org /// . /// . /// . /// @author <AUTHORNAME> <EMAIL> //-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
- The following comment block shall be used to describe class method declarations (notice the period after the short description):
/// Get XYZ Coordinates. /// This method gets the x, y, and z coordinates of a library control. /// /// /// @param _controlID /// Library control identifier /// /// @param _x /// x coordinate - Passed by reference /// /// @param _y /// y coordinate - Passed by reference /// /// @param _z /// z coordinate - Passed by reference /// /// @return /// True on success, and false if the control does not exist. /// bool getCoordinates(const int _controlID, float& x, float& y, float& z);
General
Identifiers
- Identifier names shall be unique within the first thirty-two characters. Use of capitalization does not qualify as differing in characters.
- Where an identifier consists of more than one word, lowerCamelCase shall be utilized. Exceptions to this are instances where identifiers represent class or abstract interface names, in which case UpperCamelCase shall be utilized.
- Functions shall use lowerCamelCase (both for member functions and global functions)
int thisIsAVariable; // A variable whose name uses lowerCamelCase class I_SomeAbstractInterface abstract; // An interface whose name uses UpperCamelCase class SomeConcreteInterface; // A class whose name uses UpperCamelCase
- Identifiers that represent abstract classes / interfaces? shall be prefixed with "I_".
class I_SomeAbstractInterface abstract;
- Identifiers that represent class member variables shall be prefixed with "m_".
class SomeClass { private: int m_someVariable; // Member variable of SomeClass };
- Identifiers that represent static class member variables shall be prefixed with "sm_".
class SomeSingleton { private: SomeSingleton(); // Default constructor static SomeSingleton sm_someSingleton; // Static member variable of SomeSingleton };
- Identifiers that represent global variables shall be prefixed with "g_".
int g_count; // Global variable count.
- Identifiers that represent function or method parameters shall be preceded with a single underscore ("_").
int SomeConcreteInterface::someMethod(const bool _alarmState, std::string& _message);
- Use of a double underscore ("__") prefix in an identifier shall not be used; however, they are allowed to be used to indicate depreciated functions.
- Units of measure shall be included in identifier names where appropriate.
- Avoid using identifiers which contain digits. However, single numbers may be used to differentiate between two identifiers (i.e. index2 and index3).
Lines of Code
- Blocks of code shall be indented to offset them from surrounding statements and to make the structure of the software apparent to the reader.
- Code indentation width shall be exactly four spaces within a source file.
- Associated code following the 'case' and 'default' keywords shall be indented.
Braces
- Opening and closing braces shall be aligned with the first character of the associated keyword. All statements following the opening brace shall be indented.
- Braces shall be used with 'if', 'while', 'do', and 'for', regardless of the number of statements within the control structure.
- Empty control structures shall be explicitly written with braces and a null statement with a comment to indicate that this is what is intended.
for(;;) { ; // empty on purpose }
- Accessor methods in header files are allowed to have their braces on a single line.
class SomeClass { public: int getSomeVariable() { return m_someVariable; } void setSomeVariable( const int _someVariable ) { m_someVariable = _someVariable; } private: int m_someVariable; };
Parentheses
- Arithmetic and logical expressions involving more than two terms shall use:
- Parentheses to explicitly define the order of evaluation:
int x = (a + b) * (c + d);
- A closing parenthesis placed after the last element of an expression.
if((a + b) > (c + d)) { ... }
- Parentheses to explicitly define the order of evaluation:
- Opening parentheses shall be flush with function/method names.
int someFunction();
Whitespace and Indentation
- Spaces shall be used instead of tabs for ALL whitespace.
- Preprocessor directives shall start in column one.
- Spaces shall be used around binary (except as noted) and tertiary operators, and after commas.
- There shall not be spaces around the following operators:
- Direct membership - "."
- Indirect membership - "->"
- Scope Resolution - "::"
- Direct pointer to member - ".*"
- Indirect pointer to member - "->*"
- Parenthesis - "(" or ")"
- Blank lines should be used to separate logical blocks of code, comments, and other logical groupings to improve readability.
- Readability and maintainability are the primary goals.
Legacy Code
When making minor changes to existing code, the legacy naming conventions and coding standards shall be preserved for readability / maintainability.
Implicit Comparison
Implicit comparisons are allowed for boolean types, but shall not be used for any other data type:
int i = 0; char *ptr = NULL; bool flag = true; if(flag) { ; // this is ok } if(ptr) { ; // this is not allowed } if(i) { ; // this is not allowed }
Naming Conventions
Files
- Spaces shall not be allowed in source file names
- The extension .c shall be used for all C source files.
- The extension .h shall be used for all C header files.
- The extension .cpp shall be used for all C++ source files.
- The extension .hpp shall be used for all C++ header files.
- C++ header files shall contain only one class definition.
- C++ source files shall contain only one class implementation.
- C++ source and header files shall be named after the class they implement/define.
Classes, Typedefs, Constants, and Enumerations
- Class identifiers shall begin with a capital letter.
- Typedefs shall use K&R style naming conventions and shall be postfixed with "_type".
typedef I_Service* pService_type
- Constants and enumerations shall be in ALL CAPS and words separated by an underscore.
- Classes, objects of classes, and class members (data and functions) should be named in a manner such that "class.member" and "object.member" are easy to read and make sense.
Members, Functions, and Variables
- The identifiers for members, functions, and variables shall use lowerCamelCase.
- The identifiers for argument names shall use lowerCamelCase prefixed by ("_").
- The identifiers for member data and member functions shall not contain the class identifier.
window.getWidth(); // This is correct. window.getWindowWidth(); // This is incorrect.
- Member and function names shall begin with the action name.
window.getWidth(); // This is correct. window.widthGet(); // This is incorrect.
- Accessor methods shall be consistent in referencing member data.
window.setWidth(); // These are correct. window.getWidth(); window.setWidth(); // These are incorrect. window.getH_size();
Declarations
Constants
- Hard coded numeric constants shall not be used anywhere in code except for the following:
- Loop initialization
foo = 0; do { ... } while(foo == 0);
- Increment step for a loop
for(int foo = 0; foo < maxFoo; foo = foo + 1) { ... }
- In an initializer
const int j = 0;
- Expressions which test return values on standard library functions.
- Loop initialization
- Constants shall be defined using the 'const' or 'enum' keywords rather than the '#define' preprocessor directive.
- Method and function arguments that are intended only as input arguments shall be defined constant using the 'const' keyword.
/// Short description. /// Longer function description. /// /// @param _inputParam /// Some input parameter /// /// @param _outputParam /// Some output parameter /// void someFunction(const int _inputParam, int& _outputParam);
Variables
- All applicable variables shall be initialized to some sane value.
- Variables shall be declared within the smallest possible scope.
static int sg_count = 0; // File global variable count - exists within the file scope void someFunction() { int tempCount = 0; // Variable tempCount - exists within the scope of "someFunction()" while(tempCount < g_count) { bool flag = false; // Variable flag - exists within the scope of "while( tempCount < g_count )" ... }; }
- Global variables with scope limited to the file scope in a module shall be declared with the 'static' keyword.
- Each variable shall be declared in a separate statement, followed by an inline comment if the variable name or initial value is not self-explanatory.
- Class members shall be initialized using an initialization list within the class constructor.
- The formatting should be so as to minimally impact the text when a parameter is added or removed by placing the : and comma separated list as shown here:
SomeClass::SomeClass(int _initialValue) : BaseClass(0) , m_someVariable(0) , m_someOtherVariable(0) { }
Flow Control
Loops
- 'for' loops shall be used when variables must be initialized (regardless of conditional check) or when the number and type of iterations is known.
- 'while' loops shall be used when the number of iterations may be zero.
- 'do...while' loops shall be used when the loop MUST execute at least once.
- 'break' is preferred over using flags to exit loops.
Switch / Case Statements
- Each 'switch' shall define a 'default' branch to handle unexpected cases.
- Cases that contain code and allow a "fall through" should explicitly be documented as such.
- Cases that contain local variables MUST be surrounded with { } blocks.
- Multiple 'case' statements for a single branch shall be written on separate line:
switch(a) { case 'a': // Fall through. case 'b': printf("%c\n", a); // Fall through. case 'c': printf("not a or b\n"); break; case 'd': { int x; doSomethingWith(x); printf("Did something with x: %i", x); } break; default: break; };
- If program execution passes from one branch to the next, it shall be clearly documented with a "fall through" comment.
- Unless control passes from one branch to the next, each branch shall be terminated by a break.
- Statements following the 'case' and 'default' keywords shall be indented.
Classes
- Class methods shall access class members (both data and methods) through the 'this' pointer.
float Circle::getRadius(void) { return this->m_radius; }
- Virtual methods in derived classes shall be declared with the 'virtual' keyword (even though its use is not required by C++).
- Each user-defined class shall contain
- An explicit constructor.
- An explicit destructor.
- Each user-defined class shall either inherit from 'boost::noncopyable' or contain
- An explicit copy constructor.
- An explicit overloaded assignment operator.
- Overloaded assignment operators shall return a 'const' reference to the class.
- Each user-defined class (abstract, base, or otherwise) shall declare its destructor as virtual.
- Constructors shall not invoke virtual functions.
- Constructors and destructors shall not reference global objects.
- Definitions for non-trivial class members shall be placed in the implementation file.
- Class definitions should be structured as follows:
class SomeClass : public I_SomeInterface { /// @name Types /// @{ public: typedef SomeClass* pSomeClass_type; // SomeClass pointer typedef /// @} /// @name I_SomeInterface implementation /// @{ public: /// Overloaded virtual method someBaseClassMethod() virtual void someBaseClassMethod(); /// @} /// @name SomeClass implementation /// @{ public: /// Method of class SomeClass /// void someMethod(); /// @} /// @name 'Structors /// @{ public: /// Explicit constructor for SomeClass /// explicit SomeClass(); /// Explicit destructor for SomeClass /// virtual ~SomeClass(); /// @} /// @name Member variables /// @{ private: /// Member variable of SomeClass int m_someVariable; /// @} }; // class SomeClass
- All class data should be 'private'. Classes shall not contain 'public' data without a justifiable reason. Classes shall not contain 'protected' data unless it is 'protected' to allow access by subclasses only.
- Classes defined as interfaces (by prefixing them with "I_") shall declare their explicit constructors and destructors as 'protected' and shall NOT be defined as a virtual class using the 'abstract' keyword. The "I_" prefix provides the necessary documentation that the class is abstract and the 'abstract' keyword is not part of the ISO standard.
class I_SomeInterface { /// @name 'Structors /// @{ protected: I_SomeInterface(); virtual ~I_SomeInterface(); }; // class I_SomeInterface
- 'friend' class declaration shall be used as necessary for class factories to gain appropriate access to protected 'structors.
Inheritance
- Inheritance shall be used for "is-a" relationships.
- Data member aggregation shall be used for "has-a" relationships.
Methods and Functions
General
- Locally scoped variable declarations shall not override declarations in a larger scope.
- Locally scoped variables should be separated from the method or function statements by a blank line.
Method or Function Declarations
- The return type and function identifier shall be on one line in the method or function prototype.
void someFunction(); class SomeClass { public: void someMethod(); }; // class SomeClass
- The return type and function identifier shall be on different lines in the method or function implementation.
void someFunction() { ... // Implementation of someFunction() } void SomeClass::someMethod() { ... // Implementation of SomeClass::someMethod(); }
- Non-member functions with scope limited to the module level shall be declared with the 'static' keyword.
- Methods or functions with similar parameter lists shall have a similar order in the parameters.
- Formal argument lists should either be all on one line, or all on separate lines.
void someFunction(int _someArgument, int _someOtherArgument); void someOtherFunction(int _someArgument, int _someOtherArgument, int _yetAnotherArgument);
- If a function is to change the value of an argument, the argument should be passed by reference rather than passing a pointer. Additionally, the "&" token used to define a reference shall be postfixed to the type identifier, not prefixed to the argument identifier.
void someFunction(int& _someOutputArgument);
- If a function needs read-only access to a user defined argument, the argument should be declared as a 'const' reference rather than passing by value. This eliminates the need to copy the argument onto the stack. Exceptions to this are cases where the argument is an intrinsic type (i.e. int, char, long, double, float, etc.) - in these cases, it is sufficient to simply declare the argument as a 'const' value rather than a 'const' reference.
void someFunction(const SomeClass& _someComplexInputArgument); void someOtherFunction(const int _someIntrinsicInputArgument);
Method or Function Return Types
- Methods or functions which do not return a value shall be declared as 'void'.
- Return types shall be explicitly stated for all functions and methods.
- A method or function shall never return a reference or a pointer to an automatic (e.g. nameless) variable.
- A method or function shall never return a reference or pointer to a local static variable unless that is the explicit purpose of the method or function.
Method or Function Error Handling
- Input coming from the user shall be sanity-checked
- Variables passed to a method or function that are to be output by the method or function shall be initialized.
- Errors resulting from sanity-checking or other error handling shall 'throw' a std::exception or an object that represents a subclass of std::exception. Any callers to the method or function should encapsulate the offending method or function in a 'try/catch code block. The 'catch' block should then evaluate the exception and handle it accordingly.
int main(int _argc, char* _argv[]) { try { someFunction(); } catch( std::exception _exception ) { ... // Exception handling code here } }
- Exceptions thrown by a method or function shall be documented using comments in doxygen format.
/// Description of someFunction() /// /// @throws someException /// A std::exception someException is thrown in some scenario. /// /// @throws someOtherException /// A std::exception someOtherException is thrown in some other scenario. /// void someFunction();
Accessors
- Public access to member data should be performed only through accessor methods.
- Accessor methods which do not allow the value change of a member returned shall be declared as 'const' (this allows access to data by 'const' classes).
class SomeClass { public: /// Some const accessor int getValue() const; private: /// value int m_value; }; // class SomeClass
Inline Methods or Functions
- Inline methods or functions are best used for frequently-called, short functions.
- Public interfaces must not include inline methods or functions unless part of a public template.
- Keep in mind that the 'inline' keyword is a request - your functions may or may not be compiled as 'inline'.
- Inline methods or functions shall not contain any static data.
- Inline methods or functions shall not contain array definitions.
- Inline methods or functions shall not use 'do...while', 'for', 'switch', or 'goto'.
- Recursive methods or functions shall not be declared as 'inline' as this is not supported by all compilers.
- Accessor methods which merely read or write member data should be declared inline.
- Constructors and destructors shall not be declared inline.
- Code shall not refer to the address of an inline method or function.
Files
Include Files
- Absolute path names shall not be used to specify include file locations.
- Include files shall be order independent.
- All user-defined include files shall be protected against multiple inclusion through the use of an '#ifndef...#endif' preprocessor directive pair. The '#define' name shall be "NAMESPACE_SUBNAMESPACE_..._FILENAME_IN_KNR_FORMAT_HPP_INCLUDED".
/// @file: SomeIncludeFile.hpp #ifndef ZEN_DRIVERS_SOME_INCLUDE_FILE_HPP_INCLUDED #define ZEN_DRIVERS_SOME_INCLUDE_FILE_HPP_INCLUDED namespace Zen { namespace Drivers { ... // Code } // namespace Drivers } // namespace Zen #endif // ZEN_DRIVERS_SOME_INCLUDE_FILE_HPP_INCLUDED
Debug and Test Code
If code or variables exist only for debugging / testing, they shall be conditionally compiled in / out of the code via -DDEBUG or -DTEST compilation flags.
#if DEBUG printf("debug details...\n"); ... #endif // DEBUG
Compilation and Linking
All code shall compile and link without errors or warnings. Use of -W compiler flags or warning-related #pragma compiler directives that disable the reporting of warnings is strongly discouraged unless the warnings are caused by incorrect decisions made by the compiler developers.
