Skip to content

coding standards

Tyler McDaniel edited this page Jan 7, 2020 · 5 revisions

This project aims to have maintainable code, and as such developers wishing to contribute code must make a non-trivial amount of effort to ensure that the code base remains as correct, understandable, and pleasant to work with as possible.

We are constantly revising and updating these standards for the ASGarD project. If you have suggestions on how to improve them, please feel welcome to open an issue

C++

There are many resources available, but we generally try to follow community-wide best practices. Some good resources that C++ developers should in general be familiar with, are:

Some specific choices that we want to highlight for the ASGarD project:

  • naming conventions
    • names are as descriptive as possible
    • snake_case and not camelCase or CamelCase
    • class names, variables, (including object instances) and class members start with lowercase
    • compile-time constants, including template parameters are ALL_CAPS
    • private members begin with a single underscore e.g. int _data_member
    • avoid overloading terms, including from other domains
    • East const (http://slashslash.info/eastconst/)
  • hygiene
    • avoid using namespace ... and minimize using and typedef
    • use assert() as much as possible
    • prefer value semantics (prefer to pass and return by value)
      • use rule of 5 to ensure proper move semantics
    • use RAII and no raw pointers, no raw new(), no malloc() (these are buried as deeply into owning types as possible)
    • use auto when appropriate, but not at the expense of understandability
    • avoid use of ifdefs especially, and preprocessor macros in general
    • minimize use of .data() and obtaining unprotected access to memory
    • Simplest api possible - don't pass objects by rvalue reference when it doesn't provide a performance benefit, and when we do need to do that, make it clear in the function name that we are taking ownership.
  • structure
    • source files are defined as components
      • logical unit of functionality
      • always 3 files: component.hpp, component.cpp (even if empty), component_tests.cpp
      • the component's header is always included first, skip a line, then the remaining includes are in alphabetical order
      • pragma once all header files. prefer over include guards
    • classes
      • use explicit for non-converting constructors
      • in order: public:, then protected:, then private: if present
      • in order: constructor(s), destructor, copy constructors, move constructors, operators, non mutators, mutators
      • private members begin with a single underscore e.g. int _data_member
    • variables are declared as close as possible to first use
    • variables(/classes) are initialized(/instantiated) at declaration
    • no return statements in constructors or void functions (unless returning early)
    • minimize use of early returns
    • Composition is easier to read and understand than inheritance, but functional input/output is easier to read and understand than either. Avoid superfluous classes, e.g. those that simply wrap a container.
    • push details as far downward in the implementation as possible without repeating code or incurring unacceptable (measured) performance penalties.
  • avoid using C++ exceptions
    • less confusion in (parallel) error code paths
    • enable (possibly) more compiler/std optimizations
  • avoid using C++ dynamic polymorphism (virtual functions / inheritance)
    • difficult to combine with static polymorphism
    • vtable difficulty in some accelerator runtimes / disjoint memory spaces
  • prefer signed integers; do not use unsigned to represent non-negative quantities
  • use extern template and explicitly-instantiated templates whenever possible to separate interface and implementation, reduce recompilation and binary size

CMake

  • preface all project-specific variables with ASGARD_
  • use option() instead of set(...CACHE...)
  • always prefer target_ versions of commands (e.g. target_include_directories())
  • include PRIVATE/INTERFACE/PUBLIC with all target_link_ and related commands
  • use generator expressions for more concise and robust checks, but balance with readability
Clone this wiki locally