Skip to content

Latest commit

 

History

History
275 lines (195 loc) · 7.89 KB

CONTRIBUTING.md

File metadata and controls

275 lines (195 loc) · 7.89 KB

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 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
    • 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.

    if (! sunny())
      stay_home()
  • Opening braces belong onto the same line:

    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 <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, 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.

  • 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):

    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 typename keyword only to access dependent types. For general template parameters, use class instead:

    template <class T>
    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:

    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 >, >::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;
  • 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 @cmd rather than \cmd.

  • Use // or /* and */ to define basic comments that should not be swallowed by Doxygen.