User Tools

Site Tools


coding_style

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
coding_style [2016/03/02 12:47] jmgrcoding_style [2023/04/25 16:52] (current) – external edit 127.0.0.1
Line 1: Line 1:
-====== Best Practices ======+====== Coding Style ======
  
-===== General ====== +===== 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.
-==== Tips & tricks ==== +
- +
-=== Always initialise primitive variables (other types do not require this) ===+
  
 +===== 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.
 <code cpp> <code cpp>
-// Bad +namespace MyNamespace
-int myVariable; +
-std::string myString; +
- +
-// Good +
-int myVariable{}; +
-std::string myString; +
-</code> +
- +
-=== Always set parameters as const reference if they are non-primitives === +
- +
-<code cpp> +
-// Bad +
-void myFunction(int firstParameter, std::string secondParameter)+
 { {
-    //... +    void MyFunction() 
-} +    { 
- +        while(true
-// Good +        { 
-void myFunction(int firstParameter, const std::string &secondParameter+            int value{42}; 
-+        } 
-    //...+    }
 } }
 </code> </code>
  
-=== Do not use "using namespace" outside of a function => improves readability (and only use it when needed) ===+===== Naming =====
  
-<code cpp> +^ Item ^ Example ^ Comment ^ 
-// Bad +| type (class/struct) | SomeExample | | 
-using namespace std; +| variable | someExample | | 
- +| member variable | m_someExample | | 
-void myFunction() +| global variables | gSomeExample | should be avoided | 
-{ +| function | someExample() | function names should begin with a verb: computeMap(), displayWindow() | 
-    string myString; +| member function | someExample() | | 
-    //... +| getter function | name() | not getName(), Qt convention | 
-+| setter function | setName() | | 
- +namespace | SomeExample | | 
-// Good +| template parameter | SomeExample | |
-void myFunction() +
-+
-    using namespace std; +
-     +
-    string myString; +
-    //... +
-+
- +
-// Better +
-void myFunction() +
-+
-    std::string myString; +
-    //... +
-+
-</code> +
- +
-Note that you can use namespace aliases to simplify a complex namespace hierarchy: +
- +
-<code cpp> +
-namespace myNamespace = some::complex::namespace::hierarchy; +
-</code> +
- +
-=== Use references instead of pointers if possible => easier to use (but note that Qt uses a lot of pointers for historical reasons) ===+
  
 +**Never use abbreviations in names, having long names is not an issue: all IDEs have an auto-completion feature.**
 <code cpp> <code cpp>
 // Bad // Bad
-void myFunction(MyObject *object)+void getNbApples()
 { {
-    object->function();+    int avgFuncSizePerAlloc = 42;
     //...     //...
 } }
  
 // Good // Good
-void myFunction(MyObject &object)+void getAppleCount()
 { {
-    object.function();+    int averageFunctionSizePerAllocation = 42;
     //...     //...
 } }
 </code> </code>
  
-=== Use smart pointers instead of raw/naked/dumb pointers => easier to use (but note that Qt uses its own memory management systemso if using Qt classes or Qt-based classes you will have to use //new// at least) === +**Prefer using meaningful names instead of "one letter" names like i,j,k.**
 <code cpp> <code cpp>
 // Bad // Bad
-MyObject *object = new MyObject; +for(int 0ducks.size(); ++i)
- +
-delete object; +
- +
-// Good +
-#include <memory> +
- +
-std::unique_ptr<MyObject> object{std::make_unique<MyObject>()}; +
- +
-// Good +
-#include <QObject> +
- +
-// parentObject is a pointer to a QObject +
- +
-MyObject *object = new MyObject(parentObject); +
-</code> +
- +
-=== If you have to write a function that returns multiple values, prefer returning a std::tuple instead of using reference parameters (if possible) === +
- +
-<code cpp> +
-// Bad +
-void myFunction(int &outFirstVariable, std::string &outSecondVariable) +
-+
-    outFirstVariable 42; +
-    outSecondVariable = "text"; +
-+
- +
-// Good +
-#include <tuple> +
- +
-std::tuple<int, std::string> myFunction(+
-+
-    return std::make_tuple(42, "text"); +
-+
- +
-// auto result = myFunction(); +
-// Use std::get<0>(result) to get the integer, std::get<1>(result) to get the std::string +
-</code> +
- +
-=== When using events, prefer using lambdas (nameless functions) instead of static functions === +
- +
-<code cpp> +
-#include <QPushButton> +
- +
-QPushButton *button = new QPushButton(parent); +
- +
-// Bad +
-void MyObject::onClick()+
 { {
     //...     //...
 } }
- 
-connect(button, SIGNAL(clicked()), this, SLOT(onClick())); 
  
 // Good // Good
-connect(button, &QPushButton::clicked(), [this]()+for(int duckIndex = 0; duckIndex < ducks.size(); ++duckIndex)
 { {
     //...     //...
-}); 
-</code> 
- 
-=== Never forward-declare variables === 
- 
-<code cpp> 
-// Bad 
-int i; 
-int j; 
- 
-for(; i < 10; ++i) 
-{ 
-    for(; j < 10; ++j) 
-    { 
-        //... 
-    } 
-} 
- 
-// Good 
-for(int i{}; i < 10; ++i) 
-{ 
-    for(int j{}; j < 10; ++j) 
-    { 
-        //... 
-    } 
 } }
 </code> </code>
  
-=== Never use typedef === +**Variables representing GUI elements should be suffixed by their type**
-It has been superseded by //using// since C++11. +
 <code cpp> <code cpp>
 // Bad // Bad
-typedef int MyInteger;+QPushButton *exitApplication; 
 +QLabel *duckCount;
  
 // Good // Good
-using MyInteger = int;+QPushButton *exitApplicationPushButton; 
 +QLabel *duckCountLabel;
 </code> </code>
- 
-Note that using //using// you can also set template parameters now: 
- 
-<code cpp> 
-using Integer3DVector = Generic3DVector<int>; 
-</code> 
- 
-=== Never use #define to create constants, use constexpr instead === 
-<code cpp> 
-// Bad 
-#define MY_CONSTANT_VALUE 42 
- 
-// Good 
-constexpr int MyConstantValue = 42; // Note the naming change here, caps should only be used for preprocessor defines 
-</code> 
- 
-=== Write small functions instead of huge ones === 
-=== Never call virtual functions from a constructor === 
-Reference: [[https://stackoverflow.com/questions/496440/c-virtual-function-from-constructor]] 
-===== Qt specific ===== 
- 
-==== Containers ==== 
- 
-^ C++ Standard Library ^ Qt ^ 
-| [[http://en.cppreference.com/w/cpp/container/vector|std::vector]] | Contiguous memory bloc? Yes: [[https://doc.qt.io/qt-5/qvector.html|QVector]] No: [[https://doc.qt.io/qt-5/qlist.html|QList]]* | 
-| [[http://en.cppreference.com/w/cpp/container/list|std::list]] | [[https://doc.qt.io/qt-5/qlinkedlist.html|QLinkedList]] | 
-| [[http://en.cppreference.com/w/cpp/container/set|std::set]] | [[https://doc.qt.io/qt-5/qset.html|QSet]] | 
-| [[http://en.cppreference.com/w/cpp/container/map|std::map]] | [[https://doc.qt.io/qt-5/qmap.html|QMap]] | 
-| [[http://en.cppreference.com/w/cpp/container/unordered_map|std::unordered_map]] | [[https://doc.qt.io/qt-5/qhash.html|QHash]] | 
-| [[http://en.cppreference.com/w/cpp/container/multimap|std::multimap]] | [[https://doc.qt.io/qt-5/qmultimap.html|QMultiMap]] | 
-| [[http://en.cppreference.com/w/cpp/container/unordered_multimap|std::unordered_multimap]] | [[https://doc.qt.io/qt-5/qmultihash.html|QMultiHash]] | 
- 
-*If in doubt, use [[https://doc.qt.io/qt-5/qlist.html|QList]]. 
- 
-**/!\ [[http://en.cppreference.com/w/cpp/container/list|std::list]] is not equivalent to [[https://doc.qt.io/qt-5/qlist.html|QList]] /!\** 
- 
-Prefer using [[https://doc.qt.io/qt-5/qhash.html|QHash]] and [[https://doc.qt.io/qt-5/qmultihash.html|QMultiHash]] over [[https://doc.qt.io/qt-5/qmap.html|QMap]] and [[https://doc.qt.io/qt-5/qmultimap.html|QMultiMap]] if you don't need the items to be sorted, their lookup time is smaller. 
-==== Inheritance ==== 
- 
-QObject-based: 
- * automatic memory management (no smart pointer required) 
- * constructor takes a parent QObject, defaulted to nullptr 
- * Q_OBJECT macro at the beginning of the class 
- 
-<file cpp myobject.hpp> 
-#pragma once 
- 
-#include <QObject> 
- 
-class MyObject: public QObject 
-{ 
-    Q_OBJECT 
-     
-public: 
-    MyObject(QObject *parent = nullptr); 
-    virtual ~MyObject(); 
-} 
-</file> 
- 
-<file cpp myobject.cpp> 
-#include "myobject.hpp" 
- 
-MyObject::MyObject(QObject *parent): 
-    QObject(parent) 
-{ 
-} 
- 
-MyObject::~MyObject() 
-{ 
-} 
-</file> 
- 
-QWidget-based: 
- * automatic memory management (no smart pointer required) 
- * constructor takes a parent QWidget, defaulted to nullptr 
- * Q_OBJECT macro at the beginning of the class 
- 
-<file cpp mywidget.hpp> 
-#pragma once 
- 
-#include <QWidget> 
- 
-class MyWidget: public QWidget 
-{ 
-    Q_OBJECT 
-     
-public: 
-    MyClass(QWidget *parent = nullptr); 
-    virtual ~MyClass(); 
-} 
-</file> 
- 
-<file cpp mywidget.cpp> 
-#include "mywidget.hpp" 
- 
-MyWidget::MyWidget(QWidget *parent): 
-    QWidget(parent) 
-{ 
-} 
- 
-MyWidget::~MyWidget() 
-{ 
-} 
-</file> 
- 
-Other classes: 
- * memory management through smart pointers 
- 
-==== Exceptions ==== 
- 
-[[https://isocpp.org/wiki/faq/exceptions|Exceptions]] allow the developer to use various features without having to constantly check for errors. Even if you are not using them explicitly they may be triggered by the standard library or even by //new//. Sadly, for historical reasons, [[https://doc.qt.io/qt-5/exceptionsafety.html|Qt does not support them]]. This means that if you are using a feature coming from a third party library you have to catch exceptions to prevent issues with Qt code. Note that Qt containers are exception proof however. 
- 
-If you are writing non-Qt code then you really should use exceptions and more importantly, write **exception-safe code**. Using smart pointers is a great and easy way to do this. For Qt-based code you will have to use C-style error checking based on booleans and "getErrorString" functions. This is, for me, Qt's main drawback. 
-====== Style ====== 
- 
-===== Naming ===== 
- 
-^ Item ^ Example ^ Comment ^ 
-| class name | SomeExample | | 
-| variable name | someExample | | 
-| member variable name | m_someExample | | 
- 
 ===== Code files ===== ===== Code files =====
  
 ==== Code separation ==== ==== 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'meta compiler) to find them. +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 [[http://doc.qt.io/qt-5/moc.html|moc]] (Qt'Meta-Object Compiler) to find them.
 ==== Naming ==== ==== Naming ====
  
Line 344: Line 110:
 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. 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 ====== 
  
-<code cpp> 
-#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) 
-    } 
-} 
-</code> 
coding_style.1456922856.txt.gz · Last modified: 2023/04/25 16:52 (external edit)