Skip to content

Latest commit

 

History

History
293 lines (206 loc) · 15.7 KB

CodingStyle.md

File metadata and controls

293 lines (206 loc) · 15.7 KB

Libquentier project uses clang-format tool for automatic formatting of source code. It is mandatory to run clang-format before contributing code fixes to libquentier. The version of clang-format tool must be at least 10. It is recommended to use prebuilt clang-format binaries from here.

The simplest way to run clang-format over the entire libquentier's codebase is to trigger clang-format build target. For this ensure that you have clang-format binary in your PATH (e.g. export PATH=<path-to-folder-with-clang-format-tool>:$PATH for Linux/macOS or set PATH="<path-to-folder-with-clang-format-tool>";%PATH% for Windows) and then run cmake --build . --target clang-format in your build directory. See building/installation guide for more details about the build process.

Alternatively you can run clang-format tool manually over the files you want to format:

clang-format -style=file -i <path-to-source-file-to-format>

The downside of this approach is that you need to run clang-format over every changed source file separately.

Clang-format can be integrated with various editors and IDEs. See clang-format docs for reference. For vim it is recommended to use this plugin. Here are the options for it in ~/.vimrc which are compatible with libquentier project:

let g:clang_format#command='<path-to-clang-format-binary>'
let g:clang_format#enable_fallback_style=0
let g:clang_format#auto_format=1
let g:clang_format#auto_format_on_insert_leave=0
let g:clang_format#detect_style_file=1

Style used by libquentier project is formalized in .clang-format file within the repo. You can examine the formatting options in it and see their description in this doc. Some of these rules are outlined below for reference.

Indentation

Libquentier uses spaces for indentation, not tabs. Tabs width and shift width should be exactly 4 spaces.

Curly braces usage

Always use curly braces in conditional and loop operators, even if their bodies occupy just a single line of code. The rationale is to avoid the possibility of mistakes when extending the loop or conditional operator body in future. For example, suppose you have an if operator written like this:

if (a > b)
    a = b;

Then suppose in future some additional action within the operator's body is needed. It’s unfortunately quite common to make a mistake like this:

if (a > b)
    a = b;
    b = c;

See the issue? Even though the code is formatted as if b = c is executed inside the conditional operator’s body, in reality it is executed after the conditional operator’s body:

if (a > b)
    a = b;

b = c;

In order to avoid that, please always use curly braces to clearly define the scope of the operator’s body:

if (a > b) {
    a = b;
}

if (a > b) {
    a = b;
    b = c;
}

In libquentier project opening curly brace for operators is put at the end of the first line of the conditional operator or loop operator unless the operator body consists of multiple lines. In the latter case the opening curly brace is put at the beginning of the next line. In clang-format configuration it corresponds to option BraceWrappingAfterControlStatementStyle having the value of MultiLine. See this ticket for reference.

In class declarations the opening curly brace should always be placed on the next line after the declaration:

class MyClass
{
    // < class contents >
};

In function/method implementations the opening curly brace should also always be placed on the next line after the function/method definition:

void MyClass::myMethod()
{
    // < method implementation contents >
}

void myFunction()
{
    // < function implementation contents >
}

Finally, please let the closing curly brace fully occupy its line of code, don’t append anything to it. For example, do this:

if (a > b) {
    a = b;
}
else {
    b = a;
}

but not this:

if (a > b) {
    a = b;
} else {
    b = a;
}

Line length

Lay out the source code in such a way that any single line of code is no longer than 80 columns. It is a rather hard boundary and it's not always possible to satisfy this requirement. Clang-format tool would try its best to satisfy this requirement though. The point is to have the majority of code written in a compact enough way.

Separate multi-line statements with blank lines

Multi-line statements generally read better when they are separated from other statements with blank lines. Example:

bool res = myFunctionCall(
    myFirstParam, mySecondParam, myThirdParam, myFourthParam,
    myFifthParam);

if (res) {
    // do something
}

Note the blank line between the function call and the if operator. It's easier to grasp the boundary between the two when there is a blank line between them.

Exceptions from this rule can be made in certain cases, for example, for logging macros:

QNDEBUG("Some long logging message which continues on the next line: "
    << someValueToLog);
return true;

Clang-format does not intervene with blank lines placement (other than ensuring no consequent blank lines) so it is up to the developer where to put blank lines. Use them to separate conceptually different code fragments as well as to improve the readability of code fragments.

Grouping and sorting of includes

#include statements should be split into groups separated by blank lines. Includes within each group should be sorted alphabetically. The order of include groups should be roughly the following:

  • For .cpp files: the primary header inclusion (i.e for MyClass.cpp #include "MyClass.h" should go first)
  • Local includes (i.e those using "" instead of <>)
  • Global includes from libquentier's public headers (i.e. those like <quentier/utility/Printable.h> or <quentier/types/Note.h> and such)
  • Global includes from Qt's headers (i.e. those like <QApplication>, <QWidget> and such)
  • Global includes from other 3rd party libraries i.e. boost
  • Global includes from the standard library (i.e. <iostream>, <algorithm> and such)

Example:

#include "MyClass.h"

#include "AnotherClass.h"
#include "SomeOtherClass.h"
#include "YetAnotherClass.h"

#include <quentier/types/Account.h>
#include <quentier/types/Notebook.h>
#include <quentier/types/SavedSearch.h>
#include <quentier/types/User.h>

#include <QApplication>
#include <QTextEdit>
#include <QWidget>

#include <boost/bimap.hpp>
#include <boost/multi_index.hpp>

#include <algorithm>
#include <iostream>
#include <utility>

Function/method/constructor/operator parameters

When declaring functions, methods, class constructors or operators their parameters should be laid out in either of two ways:

  • If the whole line with declaration and parameters (and keywords like const, override, noexcept and others) fits into 80 columns, let it be the single line. Example:
    void myFunction(const QString & paramOne, const QString & param2);
  • If the whole line with declaration and parameters (and keywords like const, override, noexcept and others) does not fit into 80 columns, the parameters list should start on the next line after the declaration and be indented with extra 4 spaces; the parameters should be laid out in such a way that each line fits into 80 columns. Example:
    void myOtherFunction(
        const QString & paramOne, const QString & paramTwo,
        const int paramThree, const bool paramFour);

Same rules apply for functions, methods, class constructors and operators definitions i.e. implementations:

    void myFunction(const QString & paramOne, const QString & param2)
    {
        // < function implementation contents >
    }

    void myOtherFunction(
        const QString & paramOne, const QString & paramTwo,
        const int paramThree, const bool paramFour)
    {
        // < function implementation contents >
    }

Similar rules apply when calling functions, methods or class constructors, only in those cases the rule applies to variable names, literals etc.

Line endings

The native line endings for headers and sources of libquentier are considered to be Unix-style single LFs. If you are using Windows or macOS and want to contribute some code changes to libquentier, please configure git to convert the line endings to LF on commit or on push.

Source file names

The name of a header/source file should reflect the name of the class declared/implemented in the file. In case when the header/source file contains declarations/implementations for more than one class, the file name should be either the most important class’ name or some name encompassing the nature of those multiple classes. If the header/source file doesn’t contain C++ classes at all, the file name should just provide some reasonable hint about the contents of the file.

C++ header files have .h extension and source files have .cpp extension.

Namespace

All the public code of libquentier (i.e. the declarations found in headers/quentier folder) should reside in quentier namespace. The library’s private code should reside in the same namespace as well, for convenience. It's OK to create nested namespaces in private code, if needed. Library's test code is located in test namespace nested into quentier namespace.

Class names

Class names should be capitalized. If the class name consists of several words, each word should be capitalized and there should be no underscore between the words. Like this:

class MyClass
{
    // < class contents >
};

Class/struct member variable naming

Private and protected class/struct member variable names should start with m_ followed by a word in the lower case. If the name of the member variable consists of multiple words, all the words but the first one should be capitalized, without underscores in between. For example:

class MyClass
{
private:
    double   m_averageCounter;
    bool     m_countingEnabled;
};

Public class/struct member variables may skip m_ prefix.

Also, not a strict rule but a rule of thumb: use tabular indentation to keep the names of several member variables starting at the same column. It makes them slightly more readable.

Class methods / functions naming

The first word in the name of a class method or a function should start from lower case. If the name of a method/function consists of just one word, the entire word should appear in the lower case. If the name of the method/function consists of several words, all the words but the first one should be capitalized, without underscores in between. For example:

class MyClass
{
public:
    void do();
    void doThis();
    void doThisAndThat();
};

The obvious exceptions from this rule are class constructors and destructors, since their names must match the class names in which the first word is capitalized.

Doxygen documentation for public class methods and functions

For all public class methods or functions of libquentier Doxygen documentation is required. If you add new classes or class methods or functions, please document them in Doxygen format and ensure the documentation can be built properly: first do make doc in your build directory and then go to doc/latex folder inside it and do make there. That would attempt to build a pdf from latex documentation. It can fail if, for example, there is some non-ASCII symbol within the Doxygen comments.

Comments inside the source code

This paragraph is about non-Doxygen comments inside the source code. A good rule of thumb is to try making the code so explicit that it doesn’t require commenting. However, it is not always possible/feasible due to various reasons: optimization, workarounds for some framework bugs etc. In such cases the tricky code should have a comment nearby explaining what it does and why does it have to be so incomprehensible.

Also, don’t comment out portions of code «just in case, to have it around should it ever be needed again». Having unused code around is the task of the version control system, not the source code itself.

Class declarations layout

Try to stick with the following layout of class declarations:

  • Q_OBJECT macro, if required for the class
  • inner classes
  • public typedefs (defined with using keyword of modern C++ instead of actual typedef keyword)
  • constructors - the default one, if present, comes first, then non-default constructors, then copy-constructor.
  • assignment operators, if any
  • destructor, if manually defined
  • public member variables, if any
  • Qt signals
  • public Qt slots
  • public methods
  • protected Qt slots
  • protected methods
  • private Qt slots
  • private methods
  • protected typedefs (defined with using keyword of modern C++ instead of actual typedef keyword)
  • private typedefs (defined with using keyword of modern C++ instead of actual typedef keyword)
  • protected member variables
  • privare member variables

Preferred method/function implementation layout

  • In the beginning of the method/function implementation it is wise to check the validity of input parameters values and possibly any other conditions and return with error if something is wrong and the method cannot do its job. Try to avoid multiple return points from the method/function unless these are returns with error in the beginning of the method/function body.
  • Try to make the bodies of methods/functions as small as possible. Ideally, the body of a method/function should fit a single screen or at least 2-3 screens. If it’s larger, consider refactoring the single method into a series of methods.

Signal/slot signatures

  • Use Q_SIGNALS macro instead of signals, Q_SLOTS instead of slots and Q_EMIT instead of emit, especially in the public interfaces of libquentier. The reason is that there are 3rdparty libraries which use signals, slots and emit keywords in their own code and the name clashing can create problems for libquentier users.
  • Pass signal/slot parameters by value, not by const reference, unless you are certain that the objects interacting via these signals/slots would live in the same thread. The power and convenience of Qt’s signals/slots comes from their flexibility to the thread affinity of connected objects but unless you pass parameters by value in signals/slots, it would be your job to provide the proper thread safety guarantees. There’s no reason to do this yourself when Qt can do it for you.

C++ standard used

Libquentier uses C++14 standard so usage of any features from that standard version is allowed. However, from practical perspective libquentier's code should be written in such a way that major supported compilers would have no problems building this code. At the time of this writing the oldest supported compilers are g++ 5.4.1 on Linux (the default compiler of Ubuntu Xenial, oldest still supported LTS release of Ubuntu) and Visual Studio 2017 on Windows.

Qt versions supported

Libquentier supports building with Qt no older than 5.5.1. Be careful with features relevant only for the most recent versions of Qt which can break backward compatibility with older versions of the framework. Either don't use such bleeding edge features or use ifdefs to isolate the code using them. Example:

#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
// use feature first appeared in Qt 5.7
#else
// use some replacement for older versions of Qt
#endif