C++ Style Guide for Ignite-3

Namespaces

All declarations and definitions must be in the ignite namespace. It is allowed to have namespaces nested to the ignite namespace. The technical implementation details that are not part of the public API should be in the nested detail namespaces.

It is required to mark the namespace end with the comment indicating its name.

namespace ignite {

namespace detail {

// Implementation details, not to be used directly.

} // namespace detail

// Public interface.

} // namespace ignite

Namespaces do not introduce additional indentation level.

Indentation of C++ Code

We use 4 spaces for indentation inside structs, classes, functions, nested statements, and blocks of C++ code. TAB characters are not allowed.

namespace ignite::sample {

int square(int x) {
    return x * x;
}

class counter {
public:
    unsigned int inc() {
        unsignned int ret;

        {
            std::unique_lock lock(m_mutex);

            ret = m_value++;

            if (m_value == 0)
                throw std::runtime_error("counter overflow");
        }

        log("counter incremented to " + std::to_string(ret + 1));

        return ret;
    }

private:
    unsigned int m_value = 0;

    std::mutex m_mutex;
};

} // namespace ignite::sample

Indentation of Nested Preprocessor Directives

We use 1-space indentation after the # char for nested preprocessor directives.

#ifdef HAS_FOO
# include "foo.h"
# ifdef HAS_BAR
#  include "bar.h"
# endif
#endif

Naming Conventions

We use lower case with undescore between words for almost everything:

  • file names;
  • type, function, and variable names.

For constants we use upper case with undescore between words. Macro constants must start with the IGNITE_ prefix.

For private/protected class fields it is allowed but not required to use the m_ prefix.

#ifndef IGNITE_FOO_LIMIT
# define IGNITE_FOO_LIMIT 42
#endif

namespace ignite::sample {

class foo_tester {
public:
    static constexpr unsigned int LIMIT = IGNITE_FOO_LIMIT;

    enum test_result {
        BELOW_LIMIT,
        AT_LIMIT,
        ABOVE_LIMIT,
    };

    test_result test_limit() {
        int value = m_counter().inc();
        return value < LIMIT ? BELOW_LIMIT : value == LIMIT ? AT_LIMIT : ABOVE_LIMIT;
    }

private:
    counter m_counter;
};

} // namespace ignite::sample

Template m_parameters are an exception from this rule.

template <typename T, typename IterT>
void foo(T a, IterT b) {
    // ...
}

Header File Include Order

Header files are included in order from more specific to less specific:

  • The prototype/interface header for this implementation (ie, the .h/.hh file that corresponds to this .cpp/.cc file).
  • Other headers from the same project, as needed.
  • Headers from other non-system libraries (for example, Boost.)
  • Standard C++, C, and system headers.
  • Conditionally included headers.

These groups are separated by an empty line. Inside each group files are sorted in alphabetical order.

In addition to consistency this include order has one techical advantage. It's good if each header #includes all the headers it depends on itself. And this order allows to detect sooner if there is any missing dependency.