Skip to content

Style Guide

Eric Voskuil edited this page Jul 19, 2019 · 11 revisions

Maximum of 80 characters per line [exceptions allowed for test vectors], 4 space indents, no tabs.

Each continued line should be nested by 1 indent. Parameter stacking is discouraged.

If you're nesting more than 3 levels deep then generally you are doing something wrong. Reorganize and/or break it into functions. Always prefer flat over nested, dumb over clever, slow+clear over fast+obtuse.

Avoid structured exception handling.

Use _ as opposed to - in source file names.

Variables, functions, class, enum, and typedef are lower case, define (macros) upper case, PascalCase identifiers for template types, no other mixed case.

Write self-documenting code. Names should be explanatory, not cryptic or ambiguous. Avoid abbreviations and acronyms.

Avoid Hungarian notation. Private class member variables are suffixed with _.

Functions should never be defined in headers unless required to be in every translation unit (i.e template, inline or constexpr functions).

It's nice if function names read like a sentence when used: write_to_stream(data_packet); or say_hello_to(my_friend);.

The using keyword should never be used in headers. Never declare using namespace std.

Every { begins a newline except for namespaces and closings on the same line: { ... }.

Don't internally terminate line-oriented macros, so termination will not be redundant (which produces warnings): FOO_BAR();

Spaces between all operators: x = 5 * 8 + 4 * 2.

If the order is ambiguous or confusing then use parenthesis: x = (5 * 8) + (4 * 2) but avoid otherwise.

Avoid raw pointers in C++, using smart pointers if pointers are necessary.

Use standard integer types where possible:

  • Fixed-Length Signed Integers (<cstdint> / <stdint.h>)
    • int8_t
    • int16_t
    • int32_t
    • int64_t
  • Fixed-Length Unsigned Integers (<cstdint> / <stdint.h>)
    • uint8_t
    • uint16_t
    • uint32_t
    • uint64_t
  • Unspecified Length Unsigned Integer (<cstddef> / <stddef.h>)
    • size_t

Don't use long integers as this type varies in length by platform.

Don't assume sizeof(void*) is sizeof(size_t) as it is not guaranteed and it's semantically incorrect.

Generally you should use size_t for arbitrary length representation.

Don't treat non-boolean values or expressions as boolean, even in assertions, use comparison operators.

Use char for "characters", don't use unsigned char when you mean "byte", use uint8_t.

Assume char is interpreted as Utf-8, even in VC++! We have implemented Utf-8 Everywhere.

Don't use const with copy-by-value parameters. Use foo(uint8_t bar) and foo(std::string bar) instead of foo(const uint8_t bar) and foo(const std::string bar). This prevents unnecessary constraint of the internal value, which otherwise requires another copy for modification. It also reduces function signature sizes.

Prefix out parameters with out_ and keep them to the far left of parameter list, as foo (std::string& out_bar, uint8_t). This provides consistency with functions that have both out and default default parameters.

Use const references for non-integral parameters to prevent unnecessary copying.

Use boost::filesystem::path for any part of a filesystem path, not std::string.

Each :, ; (in for loops) and , has a space after it.

No spaces between function/macros name and left parenthesis: do_bar(...). This greatly facilitates search.

Keywords such as if and for are not functions, one space before left parenthesis: while (!done) ....

Flow control should transition on new lines.

    // Bad.
    if (foo) do_bar();

    // Good.
    if (foo)
        do_bar();

Brace conditional macro execution:

    // Good.
    if (foo)
    {
        FOO_BAR(42);
    }

    // Bad.
    if (foo)
        FOO_BAR(42);

Don't put more than one logical line on a line.

    // Bad.
    something(); another_thing(); yada_yada();

    // Good.
    something(); 
    another_thing(); 
    yada_yada();

Be careful with the use of assertions, they get removed in NDEBUG builds and can cause breaks and unused variable/parameter warnings.

Use assertions only to facilitate debugging, not for error handling.

Guard all parameters of published API calls against bad data. Assertions are insufficient guards for API parameters.

Avoid direct use of numeric literals other than zero in a zero-based index. Use sizeof() if you are referring to a type size. Use constexpr or const for magic numbers.

Do not accept implicit narrowing casts. These are bugs that haven't been discovered (and produce warnings). Use static_cast but only if the conversion is first guarded.

Test subtraction for underflow and addition for overflow, before performing the operation.

Explicitly include external headers as opposed to relying on inclusion via included project source files. Implicit inclusion causes a propagation of build breaks when distant headers are modified.

Don't use global variables or goto, we are grown-ups now.

Don't tolerate warnings and don't suppress them due to laziness.

Use //// to temporarily disable code.

Use /// for public API documentation (short form) or javadoc format.

Use // for source code commentary.

Follow the closing of a scope with a blank line before a subsequent statement.

Precede comments with a blank line.

Capitalize and punctuate comments, leaving a space after the comment symbol:

// Good comment structure.
//bad comment structure

Avoid unqualified scopes

Define only one class or struct per file and match the file name to the object.

Use the following source file layouts.

IPP Format

    // license statement
    #ifndef LIBBITCOIN_FOO_IPP
    #define LIBBITCOIN_FOO_IPP

    // system includes (sorted)
    // other library includes (sorted)
    // bitcoin library includes (sorted)

    namespace libbitcoin {

    template <size_t Size>
    bool do_foo(bytes<Size>& out)
    {
        // ...
    }

    } // namespace libbitcoin

    #endif

HPP Format

    // license statement
    #ifndef LIBBITCOIN_FOO_HPP
    #define LIBBITCOIN_FOO_HPP

    // system includes (sorted)
    // other library includes (sorted)
    // bitcoin library includes (sorted)

    namespace libbitcoin {
    
    class foo
      : public bar
    {
    public:
        // typedefs
        // method declarations
        // member declarations

    private:
        // typedefs
        // method declarations
        // member declarations
    };

    } // namespace libbitcoin
    
    // function declarations
    // template includes (ipp) 

    #endif

CPP Format

    // license statement
    #include <bitcoin/foo.hpp>

    // system includes (sorted)
    // other library includes (sorted)
    // bitcoin library includes (sorted)

    namespace libbitcoin {

    // function implementations 
    // method implementations

    } // libbitcoin

H Format

    // license statement
    #ifndef LIBBITCOIN_BAR_H
    #define LIBBITCOIN_BAR_H

    // system includes (sorted)
    // other library includes (sorted)
    // bitcoin library includes (sorted)

#ifdef __cplusplus
extern "C" 
{
#endif

    // constant declarations
    // function declarations

#ifdef __cplusplus
}
#endif

#endif

C Format

    // license statement
    #include <bitcoin/bar.h>

    // system includes (sorted)
    // other library includes (sorted)
    // bitcoin library includes (sorted)
    
    // constant definitions
    // function implementations

Libbitcoin Menu

Clone this wiki locally