User Tools

Site Tools


coding_style

This is an old revision of the document!


Coding Style

Spaces vs Tabs

Qt Creator uses spaces for indentation. Since we will probably be using Qt and since Qt Creator is the default IDE when writing code using Qt, spaces should be used for indentation.

Braces

Opening and closing braces should be on a new line, in most cases: classes, functions, namespaces, arrays, etc. The only exception would be initialization.

namespace MyNamespace
{
    void MyFunction()
    {
        while(true)
        {
            int value{42};
        }
    }
}

Naming

Item Example Comment
type (class/struct) SomeExample
variable someExample
member variable m_someExample
global variables gSomeExample should be avoided
function someExample()
member function someExample()
getter function name() not getName(), Qt convention
setter function setName()
namespace SomeExample
template parameter SomeExample

Never use abbreviations in names, having long names is not an issue: all IDEs have an auto-completion feature.

// Bad
void getNbApples()
{
    int avgFuncSizePerAlloc = 42;
    //...
}
 
// Good
void getAppleCount()
{
    int averageFunctionSizePerAllocation = 42;
    //...
}

Code files

Code separation

Generally speaking, each class/struct should have its declaration in one file (header) and its definition in another file (source file). If a class/struct is very small then it can be contained in a header, for example if it only contains variables and no functions. Qt specific: note that QObject/QWidget-based classes have to have their own header file in order to the MOC (Qt's meta compiler) to find them.

Naming

Source files should have the .cpp extension (common practice) and header files the .hpp extension. Note here that the .h extension may be more common, but I personally think that the .hpp extension makes more sense since C++ headers are not generally compatible with the C language.

Header guards

Header guards are a C++-specific feature kept for historical reasons. The official way of using them is to put all the header code between #defines:

#ifndef MY_HEADER_FILE_H
#define MY_HEADER_FILE_H
 
//...
 
#endif // MY_HEADER_FILE_H

However, in practice I find this to be dangerous and I have encountered some particularly serious bugs using this method. Indeed, if you happen to copy/paste a header file you will need to remember having to change the header guard #define name. If you do not do this and if you include both header files, only the first one will be effectively included.

Another way to shoot yourself in the foot using header guards is the use of a namespace. Since preprocessor instructions cannot use them you will get into trouble if a header file within a namespace has the same name as another header file outside of it.

For these reasons, I recommend instead the use of the non-standard feature called “pragma once”:

#pragma once
 
//...

This feature in implemented in all known compilers and is both easier to use and foolproof. It has not been accepted into the C++ standard because of some particular cases, like having source files on multiple “remote mounts”: https://stackoverflow.com/questions/23696115/is-pragma-once-part-of-the-c11-standard.

Examples

#include <string>
#include <memory>
 
// An entity semantic class
class MyExampleClass final
{
public:
    // This constructor is explicit to prevent something like this: MyExampleClass test = "some text";
    explicit MyExampleClass(const std::string &myString):
        m_myVar{52},
        m_myString{myString}
    {
    }
 
    // Entity semantic: always forbid copy & assignment
    MyExampleClass(const MyExampleClass &) = delete;
    MyExampleClass &operator=(const MyExampleClass &) = delete;
 
private:
    // Variables are always at the end, because they represent an implementation detail
    int m_myVar{42};
    std::string m_myString{"value"};
};
 
// A value semantic class
class MyVector final // Always final: a value semantic class should *never* be inherited from
{
public:
    // We want to use the default implementation (wich is faster than anything we can do)
    MyVector() = default;
 
    MyVector(const MyVector &other):
        m_x(other.m_x),
        m_y(other.m_y)
    {
    }
 
    MyVector &operator=(MyVector other)
    {
        std::swap(m_x, other.m_x);
        std::swap(m_y, other.m_y);
 
        return *this;
    }
 
private:
    // m_x and m_y are initialized with the default value for the type int: 0
    // unless the initializer list in a constructor decides otherwise
    int m_x{};
    int m_y{};
};
 
void test()
{
    // I use scopes "{}" here to limit where my variables live and are accessible
    {
        // Allocation on the stack (fast)
        MyExampleClass myExampleClass{"some text"};
        // myExampleClass is destroyed here
    }
    {
        // Allocation on the heap (slower, allows polymorphism)
        std::unique_ptr<MyExampleClass> myExampleClass{std::make_unique<MyExampleClass>("some text")};
        // Or (with auto)
        auto myExampleClass{std::make_unique<MyExampleClass>("some text")};
        // myExampleClass is destroyed here (thanks to the smart pointer)
    }
}
coding_style.1457612861.txt.gz · Last modified: 2023/04/25 16:52 (external edit)