Skip to content

Instantly share code, notes, and snippets.

@sasq64
Last active April 25, 2019 11:54
Show Gist options
  • Save sasq64/0cf7ba4cc656d2fb1ff38d4022365fd8 to your computer and use it in GitHub Desktop.
Save sasq64/0cf7ba4cc656d2fb1ff38d4022365fd8 to your computer and use it in GitHub Desktop.

Revisions

  1. sasq64 revised this gist Apr 25, 2019. 1 changed file with 3 additions and 28 deletions.
    31 changes: 3 additions & 28 deletions dev_guide.md
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@ preferences. They represent -- for the most part -- an emerging
    C++14 "modern" style

    * Don't use naked new/delete
    * Use a stack variables for short lived objects.
    * Prefer passing and returning by value
    * Use unique_ptr unless you need sharing
    * Return `unique_ptr` from factory functions, they can be turned into `shared_ptr`.
    * Don't use indexed for loop when foreach is enough.
    @@ -102,33 +102,8 @@ of the class changes.
    As a special case, function arguments should of course have good names, but
    _you should not need to see them to understand the function_.

    A function should be clear by it's name and the _types_ of its arguments.

    Ideally, never have two arguments with the same type to a function.

    Examples

    **Bad:**
    `copyFile(string to, string from)`

    **Better:**
    `copyFileToFrom(string t, string f)`

    **Better:**
    `copyFileToFrom(File target, File source)`

    **Bad:**
    `drawText(int x, int y, string text)`

    **Good:**
    `drawText(Vector2 pos, string text)`

    **Bad:**
    `webPost(string url, string data)`

    **Good:**
    `webPost(URL url, JSON data)`

    The usage of a function should perferably be clear by it's name and the
    _types_ of its arguments.

    ## Error Handling

  2. sasq64 revised this gist Apr 11, 2019. 1 changed file with 5 additions and 6 deletions.
    11 changes: 5 additions & 6 deletions dev_guide.md
    Original file line number Diff line number Diff line change
    @@ -28,7 +28,6 @@ time to rename it or (better) move that logic somewhere else.
    ## Project layout

    We try to follow the _[pitchfork propsal](https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs)_
    It tries to unify defacto standards.

    We use a single, top level `CMakeLists.txt`

    @@ -41,13 +40,13 @@ The cpp file should include the headers as its first line, even if it is
    otherwise empty.

    Use `#pragma once` instead of ifdef guards; it is supported by all compilers
    and is less error prone (and faster).
    and is less error prone (and probably faster).

    Sort your includes;
    The corresponding class h-file first.
    Then other local include files.
    Then 3rd party headers.
    Last std library includes.
    * The corresponding class h-file first.
    * Then other local include files.
    * Then 3rd party headers.
    * Last std library includes.

    Try to keep dependencies out of inlude files.

  3. sasq64 revised this gist Apr 11, 2019. 1 changed file with 19 additions and 3 deletions.
    22 changes: 19 additions & 3 deletions dev_guide.md
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,13 @@ C++14 "modern" style

    ## Class Design

    * Avoid '-er' type classes (Manager, Controller, Handler etc)
    Try to avoid '-er' type classes (Manager, Controller, Handler etc). It's often
    hard to determine what their purpose is.

    Sometimes you need a class whos _only_ purpose is to handle a set of (logical)
    child classes. But they should normally be short and do only that.
    If actual business logic starts to sneak in to your manager class, it's either
    time to rename it or (better) move that logic somewhere else.

    ## Project layout

    @@ -172,11 +178,22 @@ We can then use _Doxygen_ to generate documentation.
    ### 80 Columns

    Boost uses 80 columns. The clang standard library uses 80 columns. Most lines
    are much shorter, so a longer linewidth wastes more screen real estate.
    are shorter, so a longer linewidth wastes more screen real estate.

    With `clang-format`, you don't need to do line breaking manually, it will
    format the code good enough to be readable.

    It is univerally understood that long lines are hard to read. This is why
    newspapers format things into columns. For code this can break up
    statements too much and at some point it will make it harder to read. As
    far as I know there has been no studies made to figure out where the
    "breaking point" lies, but I think the concencus in the community is that
    it is below 80 characters.

    Don't let the historical significance of the number fool you into thinking
    this is just a legacy rule. It's just a nice, even number that seems to be
    a good trade off between too long lines and two much wrapping.

    ### East const

    I prefer `fn(std::string const& arg)` instead of `fn(const std::string& arg)`.
    @@ -203,4 +220,3 @@ for everything. This guide still recommends the older more universal standard
    that is similar to Java and C#. I think this still reflects what most
    developers are used to, but it would be more correct to switch to snake case.
    We can still start types with upper case.

  4. sasq64 created this gist Apr 11, 2019.
    206 changes: 206 additions & 0 deletions dev_guide.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,206 @@
    # C++ Developer Guide

    Please note; these rules are not random; nor are they my personal
    preferences. They represent -- for the most part -- an emerging
    'de facto' standard for how modern C++ code is developed.

    ## Basics

    C++14 "modern" style

    * Don't use naked new/delete
    * Use a stack variables for short lived objects.
    * Use unique_ptr unless you need sharing
    * Return `unique_ptr` from factory functions, they can be turned into `shared_ptr`.
    * Don't use indexed for loop when foreach is enough.
    * Look to algorithms instead of complex foreach loops

    ## Class Design

    * Avoid '-er' type classes (Manager, Controller, Handler etc)

    ## Project layout

    We try to follow the _[pitchfork propsal](https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs)_
    It tries to unify defacto standards.

    We use a single, top level `CMakeLists.txt`

    We also have a boot strapping `Makefile` so you can just type `make` after cloning.

    ## File structure

    Put the interface in the header and the implementation in the cpp file.
    The cpp file should include the headers as its first line, even if it is
    otherwise empty.

    Use `#pragma once` instead of ifdef guards; it is supported by all compilers
    and is less error prone (and faster).

    Sort your includes;
    The corresponding class h-file first.
    Then other local include files.
    Then 3rd party headers.
    Last std library includes.

    Try to keep dependencies out of inlude files.

    ## Source code style

    * LLVM as base
    * Spaces for tabs
    * 80 column limit

    ```cpp
    namespace ecp {

    class StateMachine
    {
    public:
    void doSomething(std::string const& name)
    {
    if (name.empty()) {
    return;
    }
    }
    };

    } // namespace ecp
    ```

    ## Naming

    Camel case, Upper case for classes and structs, lower case for methods and
    variables.

    Namespaces should be lower case and kept short.

    Use UPPERCASE only for macros, not for enums and contants, to avoid collisions
    with existing macros.

    If you need to name your member variables differently, use a _trailing_ underscore;
    ie `myMemberVariable_`.

    Priority; where to spend most time thinking about good names

    * Classes
    * Functions
    * Member variables
    * Function arguments
    * Local variables
    * For loop / small clause variables

    ie Think _really_ hard about what a class should be called so it conveys as
    clearly as possible what it does, and _change_ the name if the responsibility
    of the class changes.

    As a special case, function arguments should of course have good names, but
    _you should not need to see them to understand the function_.

    A function should be clear by it's name and the _types_ of its arguments.

    Ideally, never have two arguments with the same type to a function.

    Examples

    **Bad:**
    `copyFile(string to, string from)`

    **Better:**
    `copyFileToFrom(string t, string f)`

    **Better:**
    `copyFileToFrom(File target, File source)`

    **Bad:**
    `drawText(int x, int y, string text)`

    **Good:**
    `drawText(Vector2 pos, string text)`

    **Bad:**
    `webPost(string url, string data)`

    **Good:**
    `webPost(URL url, JSON data)`


    ## Error Handling

    > Distinguish between errors and nonerrors. A failure is an error if and only if
    > it violates a function's ability to meet its callees' preconditions, to
    > establish its own postconditions, or to reestablish an invariant it shares
    > responsibility for maintaining. Everything else is not an error.
    >
    >Herb Sutter
    Check pre conditions at compile time if possible (static_assert).
    For errors, we use exceptions. We can catch and rethrow in jni layer.

    ## Tools

    * `clang-format` everything -- don't spend mental energy on formatting source code.
    * `Address sanitizer` and `valgrind` to find memory problems and leaks.
    * `clang-tidy` for liniting.
    * `cmake-format` to keep the CMakeLists.txt clean.

    ## Testing

    Aim for test driven design.

    We use _catch2_ and put all our unit tests under `tests/`

    Since you don't have tests for your tests;

    Test code should be understandable and _correct by inspection_.

    Also because test code is a good starting place to understand the rest of
    the code.

    Avoid special test utility methods, or test base classes. Try to write top-down,
    understandable code.

    ## Documentation

    Try to document API level methods and classes with descriptive text, using<br/>
    `//! comments like these`

    We can then use _Doxygen_ to generate documentation.

    ## Motivation / Contention

    ### 80 Columns

    Boost uses 80 columns. The clang standard library uses 80 columns. Most lines
    are much shorter, so a longer linewidth wastes more screen real estate.

    With `clang-format`, you don't need to do line breaking manually, it will
    format the code good enough to be readable.

    ### East const

    I prefer `fn(std::string const& arg)` instead of `fn(const std::string& arg)`.
    There has been a lot of debate about this, but the concesus is basically that
    the first (east const) is more logical, but the second is the standard.

    Either one is OK.

    ### Curly braces

    Some C++ programmers (mainly those used to C#) prefer to put a new line before
    every curly brace.

    Java code never puts new lines before braces.

    The de facto C++ standard is a mix, and not really a better alternative than
    either Java or C#, but since there is no standard that is universal, we may as
    well go with the de facto C++ standard.

    ### Naming

    The C++ community is moving away from _CamelCase_ towards lower _snake_case_
    for everything. This guide still recommends the older more universal standard
    that is similar to Java and C#. I think this still reflects what most
    developers are used to, but it would be more correct to switch to snake case.
    We can still start types with upper case.