Skip to content

Programming Style Guide

Matt Guerrette edited this page Apr 24, 2016 · 12 revisions

This is a very general overview and subject to change. If you have questions, contact Arsen.

C++ Specific

Object Oriented

This is C++ not C so you will probably be writing or modifying classes. If you know a Class would never have any methods or be subject to inheritance a struct is fine. Typedefs are frowned upon unless it aids in portability.

C++ 11

It's not the 90s anymore and we prefer it that way. If there is a C++11 semantic that you want to use, feel free so long as it doesn't screw up Visual Studio 2015 or GCC (min. 5.0) support.

nullptr over NULL

The only exception to this is with OpenGL function calls that can accept NULL. In that case we prefer to pass 0 rather than NULL due to some platforms that enjoy throwing warnings.

No warnings please

Try to avoid committing any code that produces warnings to the master branch. If a warning occurs during build please fix it; it's usually trivial and helps keep the build stable.

Virtual Functions

Please prefix all virtual functions with V

class IFoo
{
public:
    virtual void VTest() = 0;
};

Blocks

Use trailing braces when defining blocks.

Bad:

class Foo{
};

Good:

class Foo
{

};

Even for if/else/while/for blocks

Bad:

if (x > 0){
}

Good:

if (x > 0)
{

}

Don't use blocks if you don't need them

Bad:

if (x < 0)
{
    //Single Line if
}

Good:

if (x < 0)
    //Single Line if

An exception to this is is if you have an if/else where only one could be single line. Use blocks for both in that case.

Bad:

if (x > 0)
    //Single Line If
else 
{
   //Multi Line Else
}

Good:

if (x > 0)
{
    //Single Line If
}
else
{
    //Multi Line Else
}

Naming

Capitalize Class Names

class MyClass;
Capitalize Methods and Functions
void MyFunction();

Camel Case Members

class MyClass
{
    int myMember;
};

Camel Case Parameters

void MyMethod(int myParam);

Macros and Enums use CAPITAL_UNDERSORE

#define MY_PI 3.14159

enum MyEnum
{
    FIRST_VALUE,
    SECOND_VALUE
};

However Enum Classes are capitalized like classes

enum class MyEnumClass: char
{
    high = 'h',
    low = 'l'
};

Commenting

Don't comment code that is self explanatory

Bad:

//This adds x to y
int Add(int x, int y)
{
    return x + y;
}
  • If you are writing documentation that is a different story

Do comment code that is nuanced or complicated

Good:

//If you register a permission with a name that already exists, you will overwrite the existing permission
void RegisterPermission(const char* name, int id)
{
    //Code
}

All source files must be documented with Doxygen comments

  • Exception for headers with no source file, i.e. interfaces.

We really don't want to have to go through the daunting task back tracking through old code to document it. If you're unfamiliar with Doxygen check out the manual

##Doxygen Examples *Class

/**
\class ClassName
\ingroup RepoName(i.e. HatchitCore)
\brief A brief description about the class

A more detailed description about the class, and what its purposes
are
**/
class ClassName {};

*Function

/**
\fn bool ClassName::Function() const;
\brief Brief description of function

More detailed description about function, including what it may return
depending on what conditions.
**/
bool ClassName::Function() const { return true; }

Don't put spaces after single line comments

Bad:

// Comment

Good:

//Comment

Multi-line comments should not be on the same lines as the comment definition

Bad:

/* Multi
   Line
   Comment
*/
  • This doesn't apply to documentation stubs

Good:

/*
    Multi
    Line
    Comment
*/

Multi-line comments should be tabbed once

Bad:

/*
Multi
Line
Comment
*/

Multi-line comments should not have a bunch of asterisks

Bad:

/*Multi
 *Line
 *Comment
 */
  • This doesn't apply to copyright notices or documentation stubs

File Organization

File Naming and Folder Structure

  • Use .h for headers, .cpp for source files, and .inl for inline source files
  • Header and Source names should match or at least contain their class name
  • Use multiple Source files where appropriate (MyClass.hpp, MyClass.cpp, MyClassWindows.cpp, MyClassLinux.cpp)
  • Source files go in src/ and Headers go in include/ and Inline source files go in src/inline

File including

  • Whenever possible, prefer forward declarations instead of including the header file.
  • For typedefs and using statements, prefer including the header file over forcing a forward declaration.

Bad:

//foo.h
struct foo {};

//bar.h
using bar = foo;

//baz.h
struct foo;
using bar = foo;
struct baz { bar b; };

Good:

//foo.h
struct foo {};

//bar.h
using bar = foo;

//baz.h
#include <bar.h>
struct baz { bar b; };
  • Include all headers that your file needs to function properly. Do not rely on other headers that also include those dependencies.

Bad:

//foo.h
struct foo {};

//bar.h
#include <foo.h>
struct bar { foo f; };

//baz.h
#include <bar.h>
struct baz { foo f; bar b; };

Good:

//foo.h
struct foo {};

//bar.h
#include <foo.h>
struct bar { foo f; };

//baz.h
#include <foo.h>
#include <bar.h>
struct baz { foo f; bar b; };

Example Header File

#pragma once

/*
    Copyright notice if applicable
*/

//Pre-Include Defines

//System level includes

//Project level includes

/*
    Any important info about MyClass
    OR 
    Info for documentation engine (cldoc / doxygen / etc)
*/
class MyClass
{
    friend class MyFriend;

public:
    //Static Methods
    
    //Static Variables

    MyClass();

    //Public Methods

    //Public Variables

    virtual ~MyClass();

protected:
    //Static Methods
    
    //Static Variables

    //Protected Methods

    //Protected Variables

private:
    //Static Methods
    
    //Static Variables

    //Private Methods

    //Private Variables
};

Notes:

  • Do not use an #ifdef header guard

Example Source File

//Includes

//ALL Static Methods

//ALL Static Variables

MyClass::MyClass()
{

}

//Public Methods

MyClass::~MyClass()
{

}

/*
    Make a comment that you're now writing protected methods
*/

//Protected Methods

/*
    Make a comment that you're now writing private methods
*/

//Private Methods

Licensing

All .h and .cpp files should start with a license notification

#Functions

##Function declarations should never be more than 80 characters wide

  • new-line on every variable if you cannot fit the signature on one line.
//Bad
int MyFunction(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j ...);

//Also bad
int MyOtherFunction(int a, int b, int c, int d,
    int e, int f, int g, int h, int i, int j ...);

//Good
int MyGoodFunction(
    int a,
    int b,
    int c);

Use references as parameters when you can't accept nullptr

  • If you don't want to be passed a nullptr, use references over passing pointers to objects.
  • Expect non-const references passed into functions to possibly be modified.

#Classes / Structs

Always initialize all members of a class in each constructor

  • RAII is important! Make sure we're initializing our members to default or given values.

Prefer initializers over assignments in constructor

//Bad
Foo::Foo(int a, float b, char c)
{
    m_a = a;
    m_b = b;
    m_c = c;
}

//Good
Bar::Bar(int a, float b, char c) :
    m_a(a),
    m_b(b),
    m_c(c)
{}

#Enums

Prefer an enum class over typical enums

//Bad
enum FooEnum
{
    ENUM_VALUE_A;
};

//Good
enum class BarEnum
{
    enumValueB;
};

Attempt to nest enums inside the classes that use the enum

class Foo
{
    enum class FooType
    {
        //Enums
    };

    //Code that uses enum
};
  • If an enum is used primarily by multiple files, place definition inside separate header file.
//MyEnum.h
enum class MyEnum
{
    //Values
};

//Foo.cpp
#include <Foo.h>
#include<MyEnum.h>

void Foo::DoSomething(MyEnum e)
{
    //Do something
}

//Bar.cpp
#include<Bar.h>
#include<MyEnum.h>

void Bar::DoSomethingElse(MyEnum e)
{
    //Do something else
}

#Preprocessor

Indent nested preprocessor statements

  • Indent between the # key and the actual statement
//Bad
#if DEBUG
#if SHOULD_PRINT
    print();
#endif
#endif

//Good
#if DEBUG
#    if SHOULD_PRINT
    print();
#    endif
#endif