Programming Reference:Cpp Coding Style: Difference between revisions

From BCI2000 Wiki
Jump to navigation Jump to search
 
(2 intermediate revisions by the same user not shown)
Line 20: Line 20:


== Line Formatting ==
== Line Formatting ==
* Use tabs or spaces for '''indentation''' but please stay consistent within one file.
* 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.
* 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.
* When using spaces for indentation, indent in steps of 2 or 3 space characters.
Line 77: Line 77:


  void
  void
  MyClass::MyFunction( const SomeClass& inTheInput, int& outTheResult )
  MyClass::MyFunction(const SomeClass& inTheInput, int& outTheResult)
  {
  {
   outTheResult = mSomeDataMember;
   outTheResult = mSomeDataMember;
   for( int i = 0; i < inTheInput.NumIterations(); ++i )
   for (int i = 0; i < inTheInput.NumIterations(); ++i)
   {
   {
     int k = 4;
     int k = 4;
     while( --k > 0 )
     while (--k > 0)
    {
       outTheResult -= k;
       outTheResult -= k;
    }
   }
   }
  }
  }

Latest revision as of 09:57, 23 February 2021

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.

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
///////////////////////////////////////////////////////////////////////

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.

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.

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

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

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

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.

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