This document specifies the coding style for Broker. The style is based on [CAF's][caf-style] and [VAST's][vast-style] style guidelines. Git Workflow ============ Broker's git workflow follows [Zeek's development process][zeek-dev-proc]. In a nutshell, this means: - The `master` branch reflects the latest state of development and should always compile. - For new features and non-trivial fixes, use *topic branches* that branch off `master` with a naming convention of `topic/description` or `topic/your-name/description`. After completing work in a topic branch, check the following steps to prepare for a merge back into `master`: + Squash your commits into a single one if necessary + Create a pull request to `master` on the [Broker github][broker-github] + Ask a maintainer to review your work + Address the feedback articulated during the review + A maintainer will merge the topic branch into `master` after 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 `const` keyword preceeds the type, e.g., `const T&` as opposed to `T const&`. - `*` and `&` bind to the *type*, e.g., `const T& arg`. - 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` and `delete`. - Never use `typedef`; always write `using T = X` in favor of `typedef X T`. - Keywords are always followed by a whitespace: `if (...)`, `template <...>`, `while (...)`, etc. - Leave a whitespace after `!` to make negations easily recognizable. ```cpp if (! sunny()) stay_home() ``` - Opening braces belong onto the same line: ```cpp struct foo { void f() { } }; ``` - Do not use the `inline` keyword 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 `.cc` and files for unit tests end in `.test.cc`. - All header files should use `#pragma once` to prevent multiple inclusion. - Don't use `#include` when a forward declarations suffices. It can make sense to outsource forward declarations into a separate file per module. The file name should be `/fwd.h`. - Include order is from low-level to high-level headers, e.g., ```cpp #include #include #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`, `private` for functions and members in classes. - Mark single-argument constructors as `explicit` to 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](http://en.cppreference.com/w/cpp/language/rule_of_three). - 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_` or `set_` 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): ```cpp 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: ```cpp a_rather_long_return_type f(std::string const& x, std::string const& y) { // ... } ``` - Break before binary and ternary operators: ```cpp if (today_is_a_sunny_day() && it_is_not_too_hot_to_go_swimming()) { // ... } ``` Template Metaprogramming ------------------------ - Use the `typename` keyword only to access dependent types. For general template parameters, use `class` instead: ```cpp template auto f(typename T::type dependent) { // ... } ``` - Use `T` for generic, unconstrained template parameters and `x` for generic function arguments. Suffix both with `s` for template parameter packs: ```cpp template 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 `>`, `>::type` or `>::value` on its own line. For example: ```cpp using optional_result_type = typename std::conditional< std::is_same::value, bool, optional >::type; ``` - When dealing with "ordinary" templates, use indentation based on the position of the last opening `<`: ```cpp using type = quite_a_long_template_which_needs_a_break; ``` Comments -------- - Doxygen comments start with `///`. - Use Markdown instead of Doxygen formatters. - Use `@cmd` rather than `\cmd`. - Use `//` or `/*` and `*/` to define basic comments that should not be swallowed by Doxygen. [caf-style]: https://github.com/actor-framework/actor-framework/blob/master/CONTRIBUTING.md [vast-style]: https://github.com/mavam/vast/blob/master/CONTRIBUTING.md [zeek-dev-proc]: https://www.zeek.org/development/howtos/process.html [broker-github]: https://github.com/zeek/broker