Skip to content

Instantly share code, notes, and snippets.

@ngrodzitski
Last active October 2, 2023 17:12
Show Gist options
  • Save ngrodzitski/691ae4ffe86d8f0f1fff5d1cf8f4bb61 to your computer and use it in GitHub Desktop.
Save ngrodzitski/691ae4ffe86d8f0f1fff5d1cf8f4bb61 to your computer and use it in GitHub Desktop.

Code style

Formatting

The most reasonable approach would be relying on clang-format (or a similar tool).

Naming convention

Naming is about how to format a natural language phrase into a single C++ name (using letters’ case, underscores, abbreviations, prefixes, postfixes, etc). Regardless of the "rule" itself it must an established practices shared by all team members.

Code property Rule Example
The case snake_case, applies to everything (a few exceptions follow later)
std::string foo_bar( int baz_value );
Member data variable name
  • Class data members are m_-prefixed
  • Structs can skip this prefix if all fields are public
class channel_t
{
    // ...
private:
    int m_id;
    std::time_t m_last_activity_at;
};

struct message_t
{
    int from_id;
    int to_id;
    std::string payload;
};
Static data variable name No m_-prefix
struct foo_t
{
    static inline int upper_limit = 42;
};
Namespaces
  • snake_case
  • Prefer nested namespace definition
  • Add a comment on closing bracket
namespace foo::bar_baz
{
  // ...
}  // namespace foo::bar_baz
Functions (free/member/static) No specific rules
void foo_bar();
std::string buzz( int value );
Accessors/manipulators functions Use the same name for "getter"/"setter" functions
class some_t
{
public:
    int precision() const;
    void precision( int value );
private:
    // ...
};
Template parameter naming First_Capital_Snake
template < typename My_Key, typename My_Value >
class storage_t
{
    // ...
};
Abbreviations "Well known" abbreviations are allowed
lhs, rhs,
dst, src,
mngr, mgr,
msg,
tmp, init
sptr, ptr,
id, pos,
cb,
ec, err,
svc,
ctx,
dir,
min, max
Struct and class names Suffixed with _t for all cases (aliases follow the same rule)
class my_thing_t;
using payload_t = std::string;
Enum types
  • No _t suffix (aliases follow the same rule)
  • All items of the enumeration are not explicitly initialized or all items of the enumeration are initialized explicitly
  • Do not highlighting enum items with prefix/suffix
enum class colours { red, green, blue };
enum class http_error_codes 
{  
  ok = 200,
  not_found = 404,
  service_not_available = 503, 
  // ...
};

Function signature

Input/output parameters ordering: output last. Try to avoid non-constant references in function parameters:

void make_description( int status, std::string & short_desc, std::string & detailed_desc );

Source code organization

Physical directory layout

Project directory should be some kind of varioation to the following:

<PROJECT_ROOT_DIR>
|
+---- cmake-scripts/            // Help scripts for cmake
|
+---- build-scripts/            // Help scripts for CI and dev build.
|
+---- <project_name>/
|     |
|     +---- include/
|     |
|     +---- src/
|     |
|     +---- test/
|     |
|     +---- CMakeLists.txt      // Main project target defined here.
|
+---- CMakeLists.txt            // Project root cmake-script.

Some practical rules:

  • Associate namespaces with directories.
  • Name of a directory must be a valid C++ name and follow namespace naming convention.
  • Private headers in impl.
  • details namespace can be used without a dedicated directory.

Source Files

Naming

*.hpp for headers *.cpp for compiled files *.ipp for inlined

The name itself must be the same (for some definition of same) as the name of a major class if it has one (without _t prefix) or the name that describes the content of a given file as clear as possible. Follow the principle of least surprise.

Rules for colocating

Rules for putting classes and functions into one file (aka component, a file pair (hpp/cpp)).

  • If any entity in a given group of classes and functions cannot be used independently.
    • Exception 1: small thing (flea on an alephand). If a small piece of logic adds some value to a “big” set of logic (classes and functions) while this “big” set of logic actually can be used independently (e.g. shift operators >>, << for iostream, or fmt integrations). If this piece of logic makes sense for entities in a different source file, please, move it to another file.
    • Exception 2: domain specific set of "equal" classes (structs) introducing a vocabulary for some category of a given domain abstractions. Here the main "content" of a file is a vocabulary abstraction which is a single logical unit hence must be treated as a single class. While technically speaking some structs in a given set can be used separately we say that it is not that some specific types are used but the vocabulary is used and what is the subset of types of interest in a given code location are details of implementation using the vocabulary
  • Friendship. Friend classes and functions are favored to be located together. Unit tests can be an exceptions, however, this practice is discouraged from every day use.
  • Cyclic dependencies: in general, it is a bad idea to draw cyclic dependencies, but sometimes you just have to. In such cases, it is definitely better to locate such classes and functions in the same physical location.
  • Follow the principle of least surprise.

File Structure

Section Comment
Prologue legal notice, project attribution
Include guard (header files) #pragma once
Includes Includes for file
Content Classes and function declaretion/definitions

Include file order:

  • An associated hpp-file (for cpp files)
  • Std-header files
  • 3rd parties libs headers
  • Inhouse reusable libs
  • Other sub-projects headers.
  • This subproject headers

Banners

Use banners for definition/declaration.

//
// foo_type_t
//
 
class foo_type_t
{
  // ...
};
 
//
// bar_func()
//
 
int bar_func( int x, int y )
{
    // ...
}

Namespaces

  • All C++ definitions (hence declarations) must be defined inside namespaces.
  • Use anonymous namespaces for defining entities used only by a single cpp file.

Documentation

Always try to comment your code: classes and functions and non-trivial design choices and algorithms.

Recommendation 1: comment tell the following:

  • Purpose of a class or a function: what it does or what specific task it serves for.
  • What it returns (for non-void functions).
  • Essential behavior.
  • Describe side effects if any.
  • Reasonable preconditions/postconditions.
  • Any important notes: alert of possible intuitive perception mistakes, thread-safety properties (if entity is considered to be used in such a context), non-functional states of a class etc.
  • Write down initial author of commentary per non-trivial kludge (design inconsistencies, behaviour amusements, etc) that requires close attention.
  • Usage examples.

Recommendation 2: it is better to write 10 comments perceived by you as useless and dumb and it would appear that you were right, than not to write them in those 10 cases and it would appear that in one of these cases your dumb comment would have been very helpful for another engineer.

3rd-party libraries

To use 3rd-party libraries in a project use C++ package manager: Conan (my preference), Vcpkg, Hunter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment