Skip to content

Coding Style

Nishant Aswani edited this page Sep 4, 2019 · 1 revision

This coding style was adapted from: https://gist.github.com/lefticus/10191322 and https://github.com/xoseperez/espurna/wiki/CodingStyle

reimagine.farm Coding Style

The important thing is that code is clear and readable with an appropriate amount of whitespace and reasonable length lines. A few best practices are also mentioned.

  • Class names are UpperCamelCased
class HumiditySensor : public BaseSensor {
    [...]
}
  • Method names and variables are lowerCamelCased:
int getTemperatureValue(unsigned int unit_id) {
    [...]
}

  • Private methods and variables (even if private to the module they are declared on) are "_"-leaded
bool _i2c_enabled = false;

int _otaSetup(unsigned int idx) {
    [...]
}
  • Function and if() / for() starting bracket is in the same line and avoid single lined conditional statements:
int _otaSetup(unsigned int idx) {
    [...]
}
 

Well formed example

class MyClass
{
public:
  MyClass(int newData)
    : _data(newData)
  {
  }
  
  int getData() const
  {
    return _data;
  }
  
private:
  int _data;
};

Initialize Member Variables

...with the member initializer list

// Bad Idea
class MyClass
{
public:
  MyClass(int initValue)
  {
    _value = initValue;
  }

private:
  int _value;
};

From John Sterling's OOP Course: "The problem with the first approach is that we took the time to create that empty [variable] and then undid that work and assigned [initValue] to it. In the second approach, we never create the empty [variable]. Instead when the [variable] is created we immediately initialize it to [initValue]."

// Good Idea
// C++'s member initializer list is unique to the language and leads to
// cleaner code and potential performance gains that other languages cannot 
// match
class MyClass
{
public:
  MyClass(int initValue)
    : _value(initValue)
  {
  }

private:
  int _value;
};

Distinguish C++ Files From C Files

C++ source file should be named .cpp or NOT .c C++ header files should be named .hpp NOT .h

Use const Where Appropriate

The const keyword provides a guarantee that the function will not be modifying any parameters passed into the function. It also is a quick identifier for which functions manipulate input parameters as part of their functionality.

void displayUnitInformation const (unsigned int idx) {
    [...]
}

From John Sterling's OOP Course: "Now, we have guaranteed that display will not change anything in the current object's member variables. If we would try to change a member variable's value inside display, C++ would give an compilation error. Now display is "safe" to use on const objects."

Use nullptr

C++11 introduces nullptr which is a special type denoting a null pointer value. This should be used instead of 0 or NULL to indicate a null pointer.

Comments

Comment blocks should use //, not /* */. Using // makes it much easier to comment out a block of code while debugging.

// this function does something
int myFunc()
{
}

To comment out this function block during debugging we might do:

/*
// this function does something
int myFunc()
{
}
*/

which would be impossible if the function comment header used /* */

Never Use using In a Header File

This causes the name space you are using to be pulled into the namespace of the header file.

Include Guards

Header files must contain an distinctly named include guard to avoid problems with including the same header multiple times or conflicting with other headers from other projects

#ifndef MYPROJECT_MYCLASS_HPP
#define MYPROEJCT_MYCLASS_HPP

namespace MyProject {
class MyClass {
};
}

#endif

// Good Idea // It's clear which statements are part of the loop (or if block, or whatever) int sum = 0; for (int i = 0; i < 15; ++i) { ++sum; std::cout << i << std::endl; }


## Keep lines a reasonable length

```cpp
// Bad Idea
// hard to follow
if (x && y && myFunctionThatReturnsBool() && caseNumber3 && (15 > 12 || 2 < 3)) { 
}

// Good Idea
// Logical grouping, easier to read
if (x && y && myFunctionThatReturnsBool() 
    && caseNumber3 
    && (15 > 12 || 2 < 3)) { 
}

Use "" For Including Local Files

... <> is reserved for system includes.

// Bad Idea. Requires extra -I directives to the compiler
// and goes against standards
#include <string>
#include <includes/MyHeader.hpp>

// Worse Idea
// requires potentially even more specific -I directives and 
// makes code more difficult to package and distribute
#include <string>
#include <MyHeader.hpp>


// Good Idea
// requires no extra params and notifies the user that the file
// is a local file
#include <string>
#include "MyHeader.hpp"

Forward Declare when Possible

This:

// some header file
class MyClass;

void doSomething(const MyClass &);

instead of:

// some header file
#include "MyClass.hpp"

void doSomething(const MyClass &);

This is a proactive approach to simplify compilation time and rebuilding dependencies.

Always Use Namespaces

There is almost never a reason to declare an identifier in the global namespaces. Instead, functions and classes should exist in an appropriately named namespaces or in a class inside of a namespace. Identifiers which are placed in the global namespace risk conflicting with identifiers from other (mostly C, which doesn't have namespaces) libraries.

Avoid Compiler Macros

Compiler definitions and macros are replaced by the pre-processor before the compiler is ever run. This can make debugging very difficult because the debugger doesn't know where the source came from.

// Good Idea
namespace my_project {
  class Constants {
  public:
    static const double PI = 3.14159;
  }
}

// Bad Idea
#define PI 3.14159;