Programming Reference:Cpp Coding Style
From BCI2000 Wiki
We maintain a loose set of guidelines/rules that is aimed at readability and maintenance efficiency. These rules are not obligatory, but we ask contributors to consider them before handing in their code.
Contents |
[edit]
File Header
Source files should provide a file header containing the following information:
- an SVN file Id tag,
- the file's original author(s), preferably identified by their email addresses,
- a description of the file's contents, i.e. typically a short information about the class declared/defined in the file,
- a copyright note.
An example header in a C++ source file might look like this:
/////////////////////////////////////////////////////////////////////// // $Id$ // Author: gerd.gurke@gemuesenet.de // Description: A file header example. The header contains // an SVN Id tag, the author's email address, a short // description of the file's content, and a copyright notice. // // (C) 2000-2011, BCI2000 Project // http://www.bci2000.org ///////////////////////////////////////////////////////////////////////
[edit]
Line Formatting
- Use tabs or spaces for indentation. Please stay consistent within one file.
- Always use spaces for alignment; tab alignment will break when viewed with a different tab size.
- When using spaces for indentation, indent in steps of 2 or 3 space characters.
- Opening braces have their own line, aligned with the previous line; corresponding closing braces are placed on their own lines, at the same character position as the opening brace.
- Consider giving single statements in if, while, or for clauses their own pair of braces.
- For function definitions, return types appear on their own line, such that the function name is first in its line.
[edit]
Naming
- ALL_UPPERCASE_NAMES are reserved for preprocessor macros.
- CamelCase (uppercase letters inside words) is used to indicate word boundaries.
- Class names and namespaces begin with uppercase letters: MyNameSpace, TheClass.
- Local variables and function arguments begin with lowercase letters: theCounter, inSomeInput.
- Data members (properties) of true classes are private, and accessed via Accessor Functions.
- Setters use the prefix "Set" followed by the property's name: SetValue(). Setter functions should also return a non-const reference to the data object itself rather than void, this allows for chaining as in
float result = MyComputation().SetOrder( 5 ).SetDepth( 10 ).EvaluateAt( 4 ); - Getters use the name of the property: Value(). Omitting the "Get" prefix allows the programmer to treat getters and reference accessors identically, as far as read access is concerned.
- Reference Accessors use the name of the property, and are provided in a doubly const and a non-const version. This is important to allow const access from const object references, and to discriminate between read and write accesses (note the mPossiblyChanged flag):
const int& Value() const { return mValue; }int& Value() { mPossiblyChanged = true; return mValue; }
- Setters use the prefix "Set" followed by the property's name: SetValue(). Setter functions should also return a non-const reference to the data object itself rather than void, this allows for chaining as in
[edit]
Name prefixes
Prefixes should carry information about the scope and usage of a variable but not its type.
- In terms of scope, we discriminate between class members, static variables, global variables, and constants. The rationale behind representing scope is to avoid accidental hiding of names from a wider scope by names with a narrower scope, which may lead to difficult-to-detect bugs.
- Representing usage shows whether a variable is a pointer, or an input or output argument to a function. This makes it easier to maintain an overview over householding tasks, such as calling delete on pointers whose objects are owned by the code under review.
- Not representing type avoids name and type going out of sync--a variable's type is more likely to change than its scope and usage, and unreliable type information is less desirable than having no type information at all.
Suggested prefixes are
- m for class data members,
- s for static class members and static variables,
- g for globals,
- c for constants,
- p for pointers,
- fp for function pointers,
- in for function input arguments,
- out for function output arguments,
- io for function arguments used for input and output.
[edit]
Variable Declaration
- Always use the narrowest possible scope for a name to avoid side effects. E.g., this means to declare variables immediately before their first use, and to define loop counter variables within the loop's for statement.
- Always initialize variables of elementary type at declaration resp. constructor to avoid reading from uninitialized memory. Examples of elementary types are int, float, and const char*.
[edit]
Memory Allocation
- Allocate objects from the stack rather than the heap unless there is a good reason to use the new operator. This avoids memory leaks.
- Use STL containers rather than allocating arrays with new[]. This eliminates a number of possible errors (initialization, allocation, deallocation).
- Make sure to deallocate arrays using the delete[] operator (mind the brackets).
[edit]
Pointers and References
- When using pointers, treat the NULL case gracefully. Note that, unlike the C-library's free() function, the C++ delete and delete[] operators may be applied to NULL pointers without any effect, so no NULL check is required before applying delete/delete[].
- Use references rather than pointers wherever possible. When used in place of a pointer, a reference gives a number of hints about the code in question:
- unlike a pointer, a reference always refers to a valid object,
- the referred-to object will stay the same during the reference's lifetime,
- no code is supposed to delete the object a reference refers to.
[edit]
Control Flow
- Avoid goto, break outside switch-case blocks, and avoid multiple return statements.
- Multiple return statements are OK when they provide a readable alternative to deeply nested conditions.
[edit]
Example Function
void
MyClass::MyFunction( const SomeClass& inTheInput, int& outTheResult )
{
outTheResult = mSomeDataMember;
for( int i = 0; i < inTheInput.NumIterations(); ++i )
{
int k = 4;
while( --k > 0 )
{
outTheResult -= k;
}
}
}
[edit]
