Skip to content
This repository has been archived by the owner on Jul 16, 2022. It is now read-only.

Coding standard

jchoude edited this page Apr 17, 2013 · 2 revisions

Introduction

This standard is designed to give general style recommendations to ease everybody's work in the Fibernavigator. Some rules are very abstract and relate to general points. Other rules are more specific and apply to specific parts of the coding process.

We expect you to follow this standard. Of course, since the standard was added after more than 1000 commits to the repository, there is some code not following exactly this standard, even if it was modeled to fit with the existing style. If you find such code, please adjust it, if it doesn't involve weeks of work.

Some points are marked as MANDATORY, which indicates that the programer really should do his best to conform to the standard. Other elements are marked as SUGGESTION, which indicates that, if possible, this rule should be followed, but it is not mandatory. An example of such a rule is the placement of curly brackets. If you add code to a file already following a different standard on the placement of the brackets, use the existing standard instead. If you create a new file, try to use this standard.

The most important point here is to use the same coding style throughout a file, and throughout a class. Even if it is not the official standard, at least a new coder will be able to orient himself if the coding style of the file / class is homogeneous.

Summary of sections

Naming Conventions

The names used in the code should fit in the code, they should represent the named entity. That way, any programer will be able to easily grasp the purpose of the named entity.

If a variable represents time, weight, or some other unit, then includes the unit in the name so that other developers can more easily spot problems. For example :

int timeOutMs    // More explicit than timeOut.
int weightLbs    // More explicit than weight.

Class names

  • Name the class after what it is.
  • Do not use the parent class's name in the derived class's name.
  • Suffixes are something helpful. For example, if your system uses agents then naming something “DynamicAgent” conveys real information.
  • Use upper case letters as word separators, lower case for the rest of a word. (Pascal case)
  • First character in a name is upper case.
  • No underscore (_).
class NameOneTwo
class Name

Method names

  • Start the name with a verb, since a method / function normally executes an action.
  • Suffixes are sometimes useful :
    • Max – To mean the maximum values something can have.
    • Cnt – The current count of a running count variable.
    • Key – Key value.
  • Prefixes are sometimes useful :
    • is – To ask a question about something, for example: isInitialized()
    • get – To get a value, for example: getIterationCnt()
    • set – To set a value, for example: setIterationCnt()
  • Use the same rules as for class names, except that the names should start with a lower case letter.
class NameOneTwo
{
public:
  int execute();
  void handleError();
};

Method argument names

  • Follow the same rules as variables on the stack
class NameOneTwo
{
  public:
  int startYourEngines(Engine& someEngine, 
                       Engine& anotherEngine);
}

Class attribute names (data members)

  • Attribute names should be preceded by “m_”. (small caps)
  • After the “m_”, use the same rules as for class names, except that the first letter will be lower case.
  • ‘m’ always precedes other name modifiers.
class NameOneTwo
{
  public:
    int getVarAbc();
    int getErrorNumber();
  private:
    int m_varAbc;
    int m_errorNumber;
    String* m_pName;
}

Variable names on the stack

  • No special prefix should be used for local variables, since all other variables and members type have a prefix.
  • The first word should be lower case.
  • All following words should begin with an upper case character.
int NameOneTwo::handleError( int i_errorNumber )
{
  int            errorId = OsErr();
  Time           timeOfError;
  ErrorProcessor errorProcessor;
  Time*          pOutTime = 0;
}

Pointer variables

  • Pointer variables should be preceded by a "p".
  • Place the * close to the pointer type, not the variable name.
String* pName = new String;
String* pFullName, address; // Note: only pFullName is a pointer.

Global definitions (defines)

  • Should be all caps with "_" separators between words.
#define GLOBAL_CONSTANT 5;

Global constants

  • First letter of each word is a capital letter.
  • Each word is separated by an underscore "_".
const int Global_Variable_Which_Is_Const

Static variables

  • For the moment, there are no specific rules for static variables.

Type names

  • When possible for types based on native types, make a typedef. (This only applies to values/types that are accessible outside of a given class.)
  • Typedef names should use the same naming policy as for a class.
typedef short int  ModuleIdentifier;
typedef int        VectorIterator;

Enumeration names and labels

  • Enumeration names follow the same rule as class names.
  • All words in the enum are upper case.
  • Refrain from using the plural to name an enumeration. For example, DisplayShape instead of DisplayShapes.
enum DisplayShape { NORMAL, SPHERE, AXES, AXIS };

Variables representing a collection of entities

  • A variable representing a collection of entities should end with an "s" to represent the fact that multiple objects may be stored in it.
std::vector< Character > m_characters;    // Multiple characters.
Character                m_mainCharacter; // One character.

C++ file names and extensions

  • Header files have the extension “.h”
  • Source files have the extension “.cpp”
  • Templated class implementation should be contained in a secondary file, having the extension “.hpp”. The .hpp file should be located in the same folder as the .h file and be included directly at the end of the header declaration in the .h file.
  • .h and .cpp files should be named after the class they contain. For example, files that declares and implements the class “DynamicAgent” should be named “DynamicAgent.h” and “DynamicAgent.cpp”.

Internal file organization

Header files structure and include guards

  • Header files will usually contain the declaration of a single class, or multiple small classes that are tightly coupled or related.
  • The last line of the file should always be a blank line. This is required by some compilers.
  • Header files should always contain multiple inclusion guards, sporting the following format :
#ifndef FILENAME_H_
#define FILENAME_H_

//{… file content …}
#endif //FILENAME_H_
// This line should be blank, but it doesn't show here.

Include instructions ordering

To facilitate the reading and organizing of the #include section, the include instructions should be in the following order:

  1. Start with the header of your file. Ex: In MyClass.cpp, the first include should be #include "MyClass.h"
  2. Add files from the same directory in alphabetical order.
  3. Add files in the same project from other folders, listed in alphabetical order.
  4. Add files from project-specific librairies, for example, wxWidgets.
  5. Add files from generic librairies, for example, <iostream>.

Also, if you are using types defined in a namespace, you should add your usings under your include.

Example: :

#include "MyClass.h"

#include "AnotherClass.h"
#include "SomeClass.h"
#include "A_Folder/SomeOtherClass.h"

#include <GL/glew.h>
#include <wx/string.h>

#include <iostream>
using std::cout;
using std::endl;

Class layout within a header file

A common class layout is critical from a code comprehension point of view and for automatically generating documentation from the code.

Access levels within a class declaration

The access levels for class members should be defined in the class declaration in the following order:

  1. public
  2. protected
  3. private

What should go in Public/Protected/Private?

Public section

Only put an object’s interface in the public section. DO NOT expose any private data items in the public section. At least encapsulate access via access methods (get/set). Ideally, your method interface should make most access methods unnecessary. Do not put data in the public interface.

Protected section

The protected section should contain any data or method that could be needed by a children class through inheritance to properly expand upon the parent class and utilize its abstract concepts.

Private section

The private section should contain any data or method that must not be modified by children classes, such that their improper usage or modification would prevent parent classes from working.

Constructors / destructors

  • Constructors should be placed at the beginning of the public class definition in most cases.
  • Destructors should be placed after the constructors in the header file.
  • Destructors should however be placed LAST in the public class definition. It should be the last function in the cpp file.
  • Destructors MUST be made virtual if the class is planned to be derivable or if the class contains one or more virtual methods.

Class layout within a source file

  • The first line of a source file (.cpp) should always be the inclusion of the corresponding header file.
  • Static member initialization should be made before the implementation of the class’s methods.
  • The constructor’s implementation should be placed before any other method implementation.
  • The destructor should always be placed at the end of the file. (Facilitates the access to the destructor in the source file.)
  • The methods should be implemented in the same order they are declared within the header file. (The exception being the destructor.)
  • Methods in a source file should be separated by a comment line like the following :
//////////////////////////////////////////////////////////////////////////

Code formatting guidelines

Braces policy

Braces placement

Braces are placed under and in line with keywords :

if( condition )        while( condition )
{                      {
    ...                    ...
}                      }

When braces are needed

All if, while, do and for statements must have braces, even if there is only a single statement within the braces. For example:

if( 1 == SomeValue )
{
   SomeValue = 2;
}

This ensures that when someone adds a line of code later there already are braces and they don’t forget to add them. It also provides a more consistent look.

Indentation, tabulation and spaces policy

  • Indent using 4 spaces for each level.
  • Do not use tabs, use spaces. Most editors can substitute spaces for tabs.
  • Indent as much as needed, but no more. There are no arbitrary rules as the maximum indenting level. If the indenting level is more than 4 or 5 levels, you may think about factoring out code though.
void func()
{
    if( something bad )
    {
        if(another bad thing )
        {
            while( more input )
            {
            }
        }
     }
 }

Parenthesises

  • Do not put spaces between key words and parenthesises or functions and parenthesises.
  • Put a space between an opening parenthesis and the following element, and just before a closing parenthesis, except when it is an empty parenthesises pair.
void aMethod ( )   // Not good
void aMethod()     // Good

void anotherMethod(int firstParam, int secondParam)   // Not good
void anotherMethod( int firstParam, int secondParam ) // Good

if (aVar == false)  // Not good
if( aVar == false ) // Good

Template notation

  • When using templates, do not put spaces between keywords and the "<" and ">" symbols.
  • Put a space between an opening "<" symbol and the following element, and a space just before the close ">" symbols, except for empty "<" ">" pairs.
template <class T>    // Not good
template < class T >  // Good

std::vector<int>      // Not good
std::vector< int >    // Good

std::vector<std::pair<int, char>>      // Not good
std::vector< std::pair< int, char > >  // Good

Switch formatting

  • Falling through a case statement into the next case statement is permitted as long as a comment is included.
  • The default case should always be present and trigger an error or assertion if it should not be reached, yet is reached.
  • If you need to create variables in a switch case, put all the code of the case in a block.
switch(...)
{
    case 1:
        ...
    // FALL THROUGH

    case 2:
    {        
        int v;
        ...
    }
    break;
    default:
 }

Alignment of declaration blocks

  • For clarity, blocks of declarations should be aligned
  • The “&” and “*” tokens should be adjacent to the type, not the name.
DWORD       m_dword;
DWORD*      m_pDword;
char*       m_pChar;
int         m_int;

m_dword   = 0;
m_pDword  = NULL;
m_pChar   = NULL;
m_int     = 0;

Formatting methods with multiple arguments

We should try and make methods have as few parameters as possible. If you find yourself passing the same variables to every method, then that variable should probably be part of the class. Despite this, when a method does have a lot of parameters, format it like this :

int  AnyMethod( int   arg1,  
                int   arg2, 
                int   arg3,
                int   arg4 );

Credits

This standard has been inspired by a standard developed for another project by Olivier Vaillancourt, based on a standard available at http://home.gna.org/ngl/doc/coding/