Use clang-format tool to format your changes, see CONTRIBUTING for details.
- No
using namespace
declarations in header files. - All symbols should be declared in a namespace except for final applications.
- Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore.
// WRONG:
#include <cassert>
using namespace std;
tuple<float, float> meanAndSigma(vector<float> const& _v);
// CORRECT:
#include <cassert>
std::tuple<float, float> meanAndSigma(std::vector<float> const& _v);
-
File comment is always at top, and includes:
- Copyright.
- License.
-
Never use
#ifdef
/#define
/#endif
file guards. Prefer#pragma once
as first line below file comment. -
Prefer static const variable to value macros.
-
Prefer inline constexpr functions to function macros.
GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase.
- Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions.
- The following entities' first alpha is upper case:
- Type names.
- Template parameters.
- Enum members.
- static const variables that form an external API.
- All preprocessor symbols (macros, macro arguments) in full uppercase with underscore word separation.
All other entities' first alpha is lower case.
- Leading underscore
_
to parameter names.- Exception:
o_parameterName
when it is used exclusively for output. See also Declarations.5. - Exception:
io_parameterName
when it is used for both input and output. See also Declarations.5.
- Exception:
- Leading
c_
to const variables (unless part of an external API). - Leading
g_
to global (non-const) variables. - Leading
s_
to static (non-const, non-global) variables.
Prefer exception to bool/int return type.
- {Typename} + {qualifiers} + {name}. (TODO: Against NL.26)
- Only one per line.
- Favour declarations close to use; don't habitually declare at top of scope ala C.
- Always pass non-trivial parameters with a const& suffix.
- To return multiple "out" values, prefer returning a tuple or struct. See F.21.
- Never use a macro where adequate non-preprocessor C++ can be written.
- Make use of auto whenever type is clear or unimportant:
- Always avoid doubly-stating the type.
- Use to avoid vast and unimportant type declarations.
- However, avoid using auto where type is not immediately obvious from the context, and especially not for arithmetic expressions.
- Don't pass bools: prefer enumerations instead.
- Prefer enum class to straight enum.
// WRONG:
const double d = 0;
int i, j;
char *s;
float meanAndSigma(std::vector<float> _v, float* _sigma, bool _approximate);
Derived* x(dynamic_cast<Derived*>(base));
for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end(); ++l) {}
// CORRECT:
enum class Accuracy
{
Approximate,
Exact
};
double const d = 0;
int i;
int j;
char* s;
std::tuple<float, float> meanAndSigma(std::vector<float> const& _v, Accuracy _a);
auto x = dynamic_cast<Derived*>(base);
for (auto i = x.begin(); i != x.end(); ++i) {}
- Structs to be used when all members public and no virtual functions.
- In this case, members should be named naturally and not prefixed with
m_
- In this case, members should be named naturally and not prefixed with
- Classes to be used in all other circumstances.
- One member per line only.
- Private, non-static, non-const fields prefixed with m_.
- Avoid public fields, except in structs.
- Use
override
,final
andconst
as much as possible. - No implementations with the class declaration, except:
- template or force-inline method (though prefer implementation at bottom of header file).
- one-line implementation (in which case include it in same line as declaration).
- For a property
foo
- Member:
m_foo
; - Getter:
foo()
; also: for booleans,isFoo()
- Setter:
setFoo()
;
- Member:
- Collection conventions:
...s
meansstd::vector
e.g.using MyTypes = std::vector<MyType>
...Set
meansstd::set
e.g.using MyTypeSet = std::set<MyType>
...Hash
meansstd::unordered_set
e.g.using MyTypeHash = std::unordered_set<MyType>
- Class conventions:
...Face
means the interface of some shared concept. (e.g.FooFace
might be a pure virtual class.)
- Avoid unpronounceable names:
- If you need to shorten a name favour a pronouncable slice of the original to a scattered set of consonants.
- e.g.
Manager
shortens toMan
rather thanMgr
.
- Avoid prefixes of initials (e.g. DON'T use
IMyInterface
,CMyImplementation
) - Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments.
- A dictionary and thesaurus are your friends.
- Spell correctly.
- Think carefully about the class's purpose.
- Imagine it as an isolated component to try to decontextualise it when considering its name.
- Don't be trapped into naming it (purely) in terms of its implementation.
- Prefer
using
totypedef
. E.g.using ints = std::vector<int>
rather thantypedef std::vector<int> ints
. - Generally avoid shortening a standard form that already includes all important information:
- e.g. stick to
shared_ptr<X>
rather than shortening toptr<X>
.
- e.g. stick to
- Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently.
- e.g.
using Guard = std::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly.
- In general expressions should be roughly as important/semantically meaningful as the space they occupy.
- Comments should be doxygen-compilable, using @notation rather than \notation.
- Document the interface, not the implementation.
- Documentation should be able to remain completely unchanged, even if the method is reimplemented.
- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports).
- Be careful to scrutinise documentation that extends only to intended purpose and usage.
- Reject documentation that is simply an English transaction of the implementation.
Logging should be performed at appropriate verbosities depending on the logging message. The more likely a message is to repeat (and thus cause noise) the higher in verbosity it should be.
We use easylogging++. Some rules to keep in mind:
Level | Description |
---|---|
Global | Generic level that represents all levels. Useful when setting global configuration for all levels. |
Trace | Information that can be useful to back-trace certain events - mostly useful than debug logs. |
Debug | Informational events most useful for developers to debug application. Only applicable if NDEBUG is not defined (for non-VC++) or _DEBUG is defined (for VC++). |
Fatal | Very severe error event that will presumably lead the application to abort. |
Error | Error information but will continue application to keep running. |
Warning | Information representing errors in application but application will keep running. |
Info | Mainly useful to represent current progress of application. |
Verbose | Information that can be highly useful and vary with verbose logging level. Verbose logging is not applicable to hierarchical logging. |
Unknown | Only applicable to hierarchical logging and is used to turn off logging completely. |
Herb Sutter and Bjarne Stroustrup
- "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md)
Herb Sutter and Andrei Alexandrescu
- "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices"
Scott Meyers
- "Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition)"
- "More Effective C++: 35 New Ways to Improve Your Programs and Designs"
- "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14"