Adding a space after a unary operator is pretty uncommon and inconsistent with what we do in C. Let's better keep the C++ and the C code style consistent, unless we have a good reason to justify the difference.
9.5 KiB
You should check out the C Conventions as some section still apply (Documentation, Git, Travis).
When contributing source code, please adhere to the following coding style, which is loosely based on the Google C++ Style Guide and the coding conventions used by the C++ Standard Library. Based on the CAF coding style.
Example for the Impatient
// module/riot/example/my_class.hpp
#ifndef RIOT_EXAMPLE_MY_CLASS_HPP
#define RIOT_EXAMPLE_MY_CLASS_HPP
#include <string>
// use "//" for regular comments and "///" for doxygen
namespace riot {
namespace example {
/// This class is only being used as style guide example.
class my_class {
public:
/// Brief description. More description. Note that RIOT uses the
/// "JavaDoc-style" autobrief option, i.e., everything up until the
/// first dot is the brief description.
my_class();
/// Destructs `my_class`. Please use Markdown in comments.
~my_class();
// suppress redundant @return if you start the brief description with "Returns"
/// Returns the name of this instance.
inline const std::string& name() const {
return name_;
}
/// Sets the name of this instance.
inline void name(const std::string& new_name) {
name_ = new_name;
}
/// Prints the name to `STDIN`.
void print_name() const;
/// Does something (maybe).
void do_something();
/// Does something else.
void do_something_else();
private:
std::string name_;
};
} // namespace example
} // namespace riot
#endif // RIOT_EXAMPLE_MY_CLASS_HPP
// my_module/my_class.cpp
#include "riot/example/my_class.hpp"
#include <iostream>
namespace riot {
namespace example {
namespace {
constexpr const char default_name[] = "my object";
} // namespace <anonymous>
my_class::my_class() : name_(default_name) {
// nop
}
my_class::~my_class() {
// nop
}
void my_class::print_name() const {
std::cout << name() << std::endl;
}
void my_class::do_something() {
if (name() == default_name) {
std::cout << "You didn't gave me a proper name, so I "
<< "refuse to do something."
<< std::endl;
} else {
std::cout << "You gave me the name " << name()
<< "... Do you really think I'm willing to do something "
"for you after insulting me like that?"
<< std::endl;
}
}
void my_class::do_something_else() {
switch (default_name[0]) {
case 'a':
// handle a
break;
case 'b':
// handle b
break;
default:
handle_default();
}
}
} // namespace example
} // namespace riot
General
-
Use 4 spaces per indentation level.
-
The maximum number of characters per line is 80.
-
No tabs, ever.
-
Never use C-style casts.
-
Vertical whitespaces separate functions and are not used inside functions: use comments to document logical blocks.
-
Header filenames end in
.hpp
, implementation files end in.cpp
. -
Never declare more than one variable per line.
-
*
and&
bind to the type, e.g.,const std::string& arg
. -
Namespaces and access modifiers (e.g.,
public
) do not increase the indentation level. -
In a class, use the order
public
,protected
, and thenprivate
. -
Always use
auto
to declare a variable unless you cannot initialize it immediately or if you actually want a type conversion. In the latter case, provide a comment why this conversion is necessary. -
Never use unwrapped, manual resource management such as
new
anddelete
. -
Never use
typedef
; always writeusing T = X
in favor oftypedef X T
. -
Keywords are always followed by a whitespace:
if (...)
,template <...>
,while (...)
, etc. -
Opening braces belong to the same line:
void foo() { // ... }
-
Use standard order for readability: C standard libraries, C++ standard libraries, other libraries, (your) RIOT headers:
// some .hpp file #include <sys/types.h> #include <vector> #include "3rd/party.h" #include "riot/fwd.hpp"
RIOT includes should always be in doublequotes, whereas system-wide includes in angle brackets. In a
.cpp
file, the implemented header always comes first and the headerriot/config.hpp
can be included second if you need platform-dependent headers. -
When declaring a function, the order of parameters is: outputs, then inputs. This follows the parameter order from the STL.
-
Protect single-argument constructors with
explicit
to avoid implicit conversions.
Naming
-
All names except macros and template parameters should be lower case and delimited by underscores.
-
Template parameter names should be written in CamelCase.
-
Types and variables should be nouns, while functions performing an action should be "command" verbs. Classes used to implement metaprogramming functions also should use verbs, e.g.,
remove_const
. -
Private and protected member variables use the suffix
_
while getter and setter functions use the name without suffix:class person { public: const std::string& name() const { return name_ } void name(const std::string& new_name) { name_ = new_name; } private: std::string name_; };
-
Use
T
for generic, unconstrained template parameters andx
for generic function arguments. Suffix both withs
for template parameter packs:template <class... Ts> void print(const Ts&... xs) { // ... }
Headers
-
Each
.cpp
file has an associated.hpp
file. Exceptions to this rule are unit tests andmain.cpp
files. -
Each class has its own pair of header and implementation files and the relative path for the header file is derived from its full name. For example, the header file for
riot::example::my_class
of modulemy_module
is located atpath/to/my_module/riot/example/my_class.hpp
and the source file atpath/to/my_module/my_class.cpp
. -
All header files should use
#define
guards to prevent multiple inclusion. The symbol name is<RELATIVE>_<PATH>_<TO>_<FILE>_HPP
. -
Do not
#include
when a forward declaration suffices. -
Each library component must provide a
fwd.hpp
header providing forward declarations for all types used in the user API. -
Each library component must provide an
all.hpp
header that contains the main page for the documentation and includes all headers for the user API. -
Use
inline
for small functions (rule of thumb: 10 lines or less).
Breaking Statements
-
Break constructor initializers after the comma, use four spaces for indentation, and place each initializer on its own line (unless you don't need to break at all):
my_class::my_class() : my_base_class(some_function()), greeting_("Hello there! This is my_class!"), some_bool_flag_(false) { // ok } other_class::other_class() : name_("tommy"), buddy_("michael") { // ok }
-
Break function arguments after the comma for both declaration and invocation:
intptr_t channel::compare(const abstract_channel* lhs, const abstract_channel* rhs) { // ... }
-
Break before tenary operators and before binary operators:
if (today_is_a_sunny_day() && it_is_not_too_hot_to_go_swimming()) { // ... }
Template Metaprogramming
Despite its power, template metaprogramming came to the language pretty much by accident. Templates were never meant to be used for compile-time algorithms and type transformations. This is why C++ punishes metaprogramming with an insane amount of syntax noise. In RIOT, we make excessive use of templates. To keep the code readable despite all the syntax noise, we have some extra rules for formatting metaprogramming code.
-
Brake
using name = ...
statements always directly after=
if it does not fit in one line. -
Consider the semantics of a metaprogramming function. For example,
std::conditional
is an if-then-else construct. Hence, place the if-clause on its own line and do the same for the two cases. -
Use one level of indentation per "open" template and place the closing
>
,>::type
or>::value
on its own line. For example:using optional_result_type = typename std::conditional< std::is_same<result_type, void>::value, bool, optional<result_type> >::type; // think of it as the following (not valid C++): auto optional_result_type = conditional { if result_type == void then bool else optional<result_type> };
-
Note that this is not necessary when simply defining a type alias. When dealing with "ordinary" templates, indenting based on the position of the opening
<
is ok, e.g.:using response_handle_type = response_handle<Subtype, message, ResponseHandleTag>;
Preprocessor Macros
-
Use macros if and only if you can't get the same result by using inline functions or proper constants.
-
Macro names use the form
RIOT_<COMPONENT>_<NAME>
.
Comments
-
Doxygen comments start with
///
. -
Use Markdown instead of Doxygen formatters.
-
Use
@cmd
rather than\cmd
. -
Use
//
to define basic comments that should not be swallowed by Doxygen.