jami-docs/guidelines/Libring-coding-rules.md

9.5 KiB

This page gives rules and/or guidances to all developers that want to integrate some code to the LibRing (the Ring daemon).

  • Following rules apply to this sub-project only. Others may re-use this one in parts or whole, have their own, etc. Refer to their pages for that.
  • Some rules are strict and are strongly marked or using modals like SHALL or MUST.

!!!Not respecting these rules results in code-review rejection!!!

  • Some are guidance or recommendation (using modals like SHOULD or RECOMMEND), it's up to the developer to balance them with his own way to write code.

This wiki is a permanent Work-In-Progress system. So not all rules are written yet. That doesn't mean we don't have them yet (we == core developer members).


Rules

Language Standard

We SHALL use the C++11 standard syntax only.

For gnu-gcc and clang, use compiler command line option -std=c++11 to enforce this rule.

Maximum Line Length

You SHOULD keep your line length under the 120 characters limit.

This is mainly so that integrators can use split view to compare files, and keeping the line width below 120 columns makes this easier, even on wide screens. Configure your editing tools to this limit.

If this rule impacts negatively the readability or the validity of the code, it can be transgressed. Here are a few valid exceptions to that rule:

  1. If a comment line contains an example command or a literal URL longer than 120 characters, that line may be longer than 120 characters for ease of cut and paste.
  2. A raw string literal may exceed 120 characters. Except for test code, such literals should appear near the top of a file.
  3. An #include statement with a long path may exceed 120 columns.

Non-ASCII Characters

Non-ASCII characters should be rare, and MUST use UTF-8 formatting.

You SHALL NOT use the C++11 char16_t and char32_t character types, since they're for non-UTF-8 text. For similar reasons you also SHALL NOT use wchar_t (unless you're writing code that interacts with the Windows API, which uses wchar_t extensively).

Spaces vs. Tabs

In code files (i.e. .cpp or .h), you SHALL use spaces to indent.

One indent level is 4 (four) consecutive spaces. You should set your editor to emit spaces when you hit the tab key and when you save your file.

Header multiple-inclusion guard

All header files SHALL have an unique header guard to prevent multiple inclusion, using #ifndef xxx ... #define xxx ... #endif form.

The format of the symbol name may be _H or __H_, but you MUST ensure its uniqueness.

We also support the pragma once form ONLY in non publicly exposed headers files, as only GCC and clang are supported to build the daemon core.

Example with the foo.h file:

#ifndef FOO_H #define FOO_H
...
#endif // FOO_H

Notice the MANDATORY comment of the ending #endif.

Example with pragma version:

#pragma once

Namespace

We're using Named namespace.

  • All symbols that are not exposed SHALL be in ring namespace.
  • All symbols that are publicly exposed SHALL be in DRing namespace.

A single namespace SHALL be written like this:

namespace mynamespace {

// Follow our namespace scoped code
class Foo {
(...)
};

} // namespace mynamespace
  • A namespace definition block DOESN'T indent the encapsulated code.

  • The closing bracket SHALL be commented with the related namespace.

A nested namespace may be written like this:

namespace mynamespace1 { namespace mynamespace2 { namespace mynamespace3 {
// some code that belong to mynamespace1::mynamespace2::mynamespace3
} // namespace mynamespace1::mynamespace2::mynamespace3
  • This single-line notation is used until C++ standard adopts the form namespace A::B::C {

If you need to express different code in multiple namespace you may use for example the form:

namespace mynamespace1 {

namespace mynamespace2 {
// some code that belong to mynamespace1::mynamespace2
} // namespace mynamespace1::mynamespace2

namespace mynamespace3 {
// some code that belong to mynamespace1::mynamespace3
} // namespace mynamespace1::mynamespace3

} // namespace mynamespace1

Namespaces wrap the entire source file after includes, gflags definitions/declarations, and forward declarations of classes from other namespaces.

  • You SHALL NOT use a using-directive to make all names from a namespace available. Particullary true for the standard library (std::)
// Forbidden -- This pollutes the namespace.
using namespace foo;
  • You may use a using-declaration anywhere in a .cpp file, and in functions, methods or classes in .h files.
// OK in .cpp files.
// But must be in a function, method or class in .h files.
using ::foo::bar;

Naming

  • General Rules

Names SHOULD be descriptive; avoid abbreviations.

Give as descriptive a name as possible, within reason. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.

  • Type Names

Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum.

  • Class Data Members

All private data members of classes, both static and non-static, are named like ordinary nonmember variables, but with a trailing underscore.

class Call {
  ...
 private:
  int number_;  // OK - underscore at end.
  std::string peerName_;   // OK.
  static std::vector<Call> subCalls_;  // OK.
};

Casting

  • You SHOULD avoid C casts, prefer C++ casts (static_cast, const_cast, reinterpret_cast)

Rationale: Both reinterpret_cast and C-style casts are dangerous, but at least reinterpret_cast won't remove the const modifier

  • It's RECOMMENDED to use brace initialization for conversion of POD types like int{myFloat} instead of (int)myFloat

Rationale: When refactoring code, the compiler will instantly let you know if the cast would become dangerous.

Using brace initialization ({}) permits compile-time detection of dangerous narrowing conversion, not detected with classic constructor call (with brackets ()).

Static and Global Variables

Static or global variables of class type are FORBIDDEN: this is the source of static initialization order fiasco.

Take a look to this library talking about the issue and trying to remove them as possible: https://cryptopp.com/wiki/Static_Initialization_Order_Fiasco

However, such variables are allowed if they are constexpr: they have no dynamic initialization or destruction.

Objects with static storage duration, including global variables, static variables, static class member variables, and function static variables, must be Plain Old Data (POD): only ints, chars, floats, or pointers, or arrays/structs of POD.

Microsoft Compiler Compliance

Guidelines for compliance with Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23918

If alternative tokens for logical operators (and, not, or etc.) are used, you must include

// source or header file in which alternate logical operator tokens are used

#include <ciso646>

Initializing (nested) structures should be complete and implicit without the use of designators

struct A {
   struct B {
       int ivalue;
   } child;
   bool bvalue;
};

A wrong = { .child = { .ivalue = 1 }, .bvalue = true };     //error in MSVC
A right = { { 1 }, true };                                  //OK in MSVC
A right = { true };                                         //OK but will assign ivalue to true

Automatic Formating

Correctly format your code using following astyle (versions < 3.0) command:

astyle --indent=spaces=4 --max-code-length=120 --attach-namespaces --attach-inlines --attach-extern-c --indent-switches --indent-preproc-define --indent-col1-comments --min-conditional-indent=0 --break-blocks --keep-one-line-blocks --pad-header --unpad-paren --align-pointer=type --align-reference=type --close-templates <input-files>

With astyle >= v3.0, we recommand to add these options too:

--pad-oper --pad-comma

Future

This section is related to future features related to Coding Rules chapter.

CPPLINT

Google has made an automatic tool to check coding rules conformance of C++ sources. We may use it, with modifications, to help contributors to check their code in an automatic way, before submitting it for review. This tool is written in Python and available from (in its origin form) here: http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py

The modified version for Ring is under development/testing and is available in the tools directory as cpplint.py3.

You can see it online using our GitHub mirror: https://github.com/savoirfairelinux/ring-daemon/blob/master/tools/cpplint.py3

Helpers

Check all sources on ring-daemon project (adapt verbose level as needed):

find src bin -regex '.*\.\(cpp\|h\)' -a -not -regex '.*\.adaptor.h' | xargs cpplint.py --linelength=120 --filter=-build/header_guard --verbose=5 {}