Jump to content

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.

Line Formatting

  • No tab characters (please configure your editor to insert spaces instead).
  • Indentation 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.
  • For function definitions, return types appear on their own line, such that the function name is first in its line.

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; }

Name prefixes

Prefixes should carry information about the scope and usage of a variable but not its type.

  • 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 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.

Variable Declaration

  • Always use the narrowest possible scope for a name to avoid side effects.
  • 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*.

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).

Pointers and References

  • When using pointers, treat the NULL case gracefully.
  • 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.

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.

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;
  }
}

See also

Programming Reference:Project Settings