7.9 KiB
This document specifies the coding style for Broker. The style is based on CAF's and VAST's style guidelines.
Git Workflow
Broker's git workflow follows Zeek's development process. In a nutshell, this means:
-
The
masterbranch reflects the latest state of development and should always compile. -
For new features and non-trivial fixes, use topic branches that branch off
masterwith a naming convention oftopic/descriptionortopic/your-name/description. After completing work in a topic branch, check the following steps to prepare for a merge back intomaster:- Squash your commits into a single one if necessary
- Create a pull request to
masteron the Broker github - Ask a maintainer to review your work
- Address the feedback articulated during the review
- A maintainer will merge the topic branch into
masterafter it passes the code review
Commit Messages
When writing commit messages, use the following style:
-
The first line succinctly summarizes the changes in no more than 50 characters. It is capitalized and written in and imperative present tense: e.g., "Fix bug" as opposed to "Fixes bug" or "Fixed bug".
-
The first line does not contain a dot at the end. (Think of it as the header of the following description.)
-
The second line is empty.
-
Optional long descriptions as full sentences begin on the third line, indented at 72 characters per line.
Coding Style
General
-
Use 2 spaces per indentation level.
-
No tabs, ever.
-
No C-style casts, ever.
-
80 characters max per line.
-
Minimize vertical whitespace within functions. Use comments to separate logical code blocks.
-
Namespaces and access modifiers (e.g.,
public) do not increase the indentation level. -
The
constkeyword preceeds the type, e.g.,const T&as opposed toT const&. -
*and&bind to the type, e.g.,const T& arg. -
Always use
autoto 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
newanddelete. -
Never use
typedef; always writeusing T = Xin favor oftypedef X T. -
Keywords are always followed by a whitespace:
if (...),template <...>,while (...), etc. -
Leave a whitespace after
!to make negations easily recognizable.if (! sunny()) stay_home() -
Opening braces belong onto the same line:
struct foo { void f() { } }; -
Do not use the
inlinekeyword unless to avoid duplicate symbols. The compiler does a better job at figuring out what functions should be inlined.
Header
-
Header filenames end in
.hh, implementation filenames in.ccand files for unit tests end in.test.cc. -
All header files should use
#pragma onceto prevent multiple inclusion. -
Don't use
#includewhen a forward declarations suffices. It can make sense to outsource forward declarations into a separate file per module. The file name should be<MODULE>/fwd.h. -
Include order is from low-level to high-level headers, e.g.,
#include <sys/types.h> #include <memory> #include <3rd/party.hpp> #include "broker/endpoint.hh"Within each section the order should be alphabetical. Broker includes should always be in double quotes and relative to the source directory, whereas system-wide includes in angle brackets.
-
As in the standard library, the order of parameters when declaring a function is: inputs, then outputs. API coherence and symmetry trumps this rule, e.g., when the first argument of related functions model the same concept.
Classes
-
Use the order
public,proctected,privatefor functions and members in classes. -
Mark single-argument constructors as
explicitto avoid implicit conversions. -
The order of member functions within a class is: constructors, operators, mutating members, accessors.
-
Friends first: put friend declaration immediate after opening the class.
-
Use structs for state-less classes or when the API is the struct's state.
-
Prefer types with value semantics over reference semantics.
-
Use the rule of zero or rule of five.
-
When providing a move constructor and move-assignment operator, declare them as
noexcept.
Naming
-
Class names, constants, and function names are lowercase with underscores.
-
Template parameter types should be written in CamelCase.
-
Types and variables should be nouns, while functions performing an action should be "command" verbs. Getter and setter functions should be nouns. We do not use an explicit
get_orset_prefix. Classes used to implement metaprogramming functions also should use verbs, e.g.,remove_const. -
All library macros should start with
BROKER_to avoid potential clashes with external libraries. -
Names of (i) classes/structs, (ii) functions, and (iii) enums should be lower case and delimited by underscores.
-
Put non-API implementation into namespace
detail. -
Member variables have an underscore (
_) as suffix, unless they constitute the public interface. Getters and setters use the same member name without the suffix. -
Put static non-const variables in an anonymous namespace.
Breaking Statements
-
Break constructor initializers after the comma, use two 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:
a_rather_long_return_type f(std::string const& x, std::string const& y) { // ... } -
Break before binary and ternary operators:
if (today_is_a_sunny_day() && it_is_not_too_hot_to_go_swimming()) { // ... }
Template Metaprogramming
-
Use the
typenamekeyword only to access dependent types. For general template parameters, useclassinstead:template <class T> auto f(typename T::type dependent) { // ... } -
Use
Tfor generic, unconstrained template parameters andxfor generic function arguments. Suffix both withsfor template parameter packs:template <class T, class... Ts> auto f(T x, Ts... xs) { // ... } -
Break
using name = ...statements always directly after=if they do not fit in one line. -
Use one level of indentation per "open" template and place the closing
>,>::typeor>::valueon its own line. For example:using optional_result_type = typename std::conditional< std::is_same<result_type, void>::value, bool, optional<result_type> >::type; -
When dealing with "ordinary" templates, use indentation based on the position of the last opening
<:using type = quite_a_long_template_which_needs_a_break<std::string, double>;
Comments
-
Doxygen comments start with
///. -
Use Markdown instead of Doxygen formatters.
-
Use
@cmdrather than\cmd. -
Use
//or/*and*/to define basic comments that should not be swallowed by Doxygen.