Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CppNorth - Typical C++, but Why?

CppNorth - Typical C++, but Why?

The C++ type system is both very weak and very strong. In this presentation I will show you how using the strengths of the type system makes your code better. I will show you how types...

* prevents incorrect code from compiling
* improves readability of code
* reduces the risks when changing code

and I will show you how very simple changes to your code will take you far in the desired direction.

Björn Fahller

July 18, 2023
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 1/126

    View Slide

  2. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 2/126
    Typical C++, But Why?
    Björn Fahller
    https://puzzlemontage.crevado.com/

    View Slide

  3. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 3/126
    Typical C++, But Why?

    View Slide

  4. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 4/126
    Typical C++, But Why?

    View Slide

  5. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 5/126
    Typical C++, But Why?

    View Slide

  6. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 6/126
    Typical C++, But Why?

    View Slide

  7. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 7/126
    Typical C++, But Why?
    Björn Fahller
    https://puzzlemontage.crevado.com/

    View Slide

  8. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 8/126
    C++ type system in a nutshell

    C++ has a small standard set of jigsaw puzzle
    piece shapes, which often fit in the wrong
    places

    View Slide

  9. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 9/126
    C++ type system in a nutshell

    C++ has a small standard set of jigsaw puzzle
    piece shapes, which often fit in the wrong
    places

    C++ allows you to create your own shapes and
    make them as generic or specific as you want

    View Slide

  10. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 10/126
    C++ type system in a nutshell

    C++ has a small standard set of jigsaw puzzle
    piece shapes, which often fit in the wrong
    places

    C++ allows you to create your own shapes and
    make them as generic or specific as you want

    Developers usually create pieces that make up
    the structure of the program, but rarely for the
    information passed between the pieces

    View Slide

  11. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 11/126
    C++ type system in a nutshell

    C++ has a small standard set of jigsaw puzzle
    piece shapes, which often fit in the wrong
    places

    C++ allows you to create your own shapes and
    make them as generic or specific as you want

    Developers usually create pieces that make up
    the structure of the program, but rarely for the
    information passed between the pieces

    View Slide

  12. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 12/126
    C++ type system in a nutshell

    C++ has a small standard set of jigsaw puzzle
    piece shapes, which often fit in the wrong
    places

    C++ allows you to create your own shapes and
    make them as generic or specific as you want

    Developers usually create pieces that make up
    the structure of the program, but rarely for the
    information passed between the pieces
    I intend to make you change this
    by showing a number of examples
    and how they can be improved.

    View Slide

  13. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 13/126
    Wrong argument
    https://puzzlemontage.crevado.com/

    View Slide

  14. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 14/126
    std::string read_user_input();
    std::string sanitize_input(const std::string&);
    std::string query_user_data(const std::string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }
    Wrong argument

    View Slide

  15. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 15/126
    std::string read_user_input();
    std::string sanitize_input(const std::string&);
    std::string query_user_data(const std::string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }
    Wrong argument

    View Slide

  16. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 16/126
    std::string read_user_input();
    std::string sanitize_input(const std::string&);
    std::string query_user_data(const std::string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }
    Wrong argument

    View Slide

  17. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 17/126
    std::string read_user_input();
    std::string sanitize_input(const std::string&);
    std::string query_user_data(const std::string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }
    Wrong argument

    View Slide

  18. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 18/126
    std::string read_user_input();
    std::string sanitize_input(const std::string&);
    std::string query_user_data(const std::string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }
    Wrong argument
    https://xkcd.com/327/

    View Slide

  19. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 19/126
    std::string read_user_input();
    std::string sanitize_input(const std::string&);
    std::string query_user_data(const std::string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }
    Wrong argument
    Oops!
    But what to
    do?

    View Slide

  20. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 20/126
    Wrong argument
    std::string read_user_input();
    sanitized_string sanitize_input(const std::string&);
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }

    View Slide

  21. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 21/126
    Wrong argument
    std::string read_user_input();
    sanitized_string sanitize_input(const std::string&);
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }

    View Slide

  22. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 22/126
    Wrong argument
    std::string read_user_input();
    sanitized_string sanitize_input(const std::string&);
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    } struct sanitized_string {
    std::string value;
    };

    View Slide

  23. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 23/126
    Wrong argument
    std::string read_user_input();
    sanitized_string sanitize_input(const std::string&);
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    } struct sanitized_string {
    std::string value;
    };

    View Slide

  24. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 24/126
    Wrong argument
    std::string read_user_input();
    sanitized_string sanitize_input(const std::string&);
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(input);
    process_result(result);
    }
    error: invalid initialization of reference of type 'const sanitized_string&'
    from expression of type 'std::__cxx11::basic_string'
    | auto result = query_user_data(input);
    | ^~~~~
    ~~~~~~~~~~~~~~~~~~~~~~
    struct sanitized_string {
    std::string value;
    };

    View Slide

  25. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 25/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };

    View Slide

  26. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 26/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };

    View Slide

  27. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 27/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    void work(int);
    class C {
    public:
    operator int() const;
    };
    void f(C c)
    {
    work(c);
    }

    View Slide

  28. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 28/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    void work(int);
    class C {
    public:
    operator int() const;
    };
    void f(C c)
    {
    work(c);
    }
    work() accepts an int,
    but c is not an int. The
    conversion operator is
    called to get an int.

    View Slide

  29. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 29/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    void work(int);
    class C {
    public:
    operator int() const;
    };
    void f(C c)
    {
    work(c);
    }
    work() accepts an int,
    but c is not an int. The
    conversion operator is
    called to get an int.
    Conversion to int. You
    almost always want such
    an operator to be const.

    View Slide

  30. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 30/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    void work(int);
    class C {
    public:
    operator int() const;
    };
    void f(C c)
    {
    work(c);
    }
    void work(int);
    class C {
    public:
    explicit operator int() const;
    };
    void f(C c)
    {
    work(static_cast(c));
    }

    View Slide

  31. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 31/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    void work(int);
    class C {
    public:
    operator int() const;
    };
    void f(C c)
    {
    work(c);
    }
    void work(int);
    class C {
    public:
    explicit operator int() const;
    };
    void f(C c)
    {
    work(static_cast(c));
    }
    Making the conversion
    operator explicit
    eliminates the risk for
    accidental conversions.

    View Slide

  32. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 32/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitize_input(input);
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    void work(int);
    class C {
    public:
    operator int() const;
    };
    void f(C c)
    {
    work(c);
    }
    void work(int);
    class C {
    public:
    explicit operator int() const;
    };
    void f(C c)
    {
    work(static_cast(c));
    }
    Making the conversion
    operator explicit
    eliminates the risk for
    accidental conversions.
    You have to say you
    want a conversion.

    View Slide

  33. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 33/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitized_string{input};
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };

    View Slide

  34. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 34/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitized_string{input};
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    Private, so cannot
    accidentally modify
    the underlying value

    View Slide

  35. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 35/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitized_string{input};
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    Private, so cannot
    accidentally modify
    the underlying value
    Explicit effort needed to
    get the string representation

    View Slide

  36. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 36/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitized_string{input};
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    }
    class sanitized_string {
    public:
            // \throws illegal_query
    explicit sanitized_string(std::string);
    explicit operator const std::string&() const;
    private:
    std::string value;
    };
    Private, so cannot
    accidentally modify
    the underlying value
    Explicit effort needed to
    get the string representation
    Very difficult to have an invalid
    sanitized_string object.

    View Slide

  37. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 37/126
    Wrong argument
    std::string read_user_input();
    std::string query_user_data(const sanitized_string&);
    void process_result(const std::string&);
    class illegal_query { … };
    void react_to_user_input() {
    auto input = read_user_input();
    auto sanitized_input = sanitized_string{input};
    log("input user data=", sanitized_input)
    auto result = query_user_data(sanitized_input);
    process_result(result);
    } Code is:

    Very clean

    Very easy to read

    Very correct

    View Slide

  38. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 38/126
    Wrong argument

    Even a very simple struct eliminates a whole
    class of runtime errors

    Private data and throwing constructor makes
    the right thing easy and the wrong thing hard

    Almost always use explicit for constructors
    and conversion operators

    View Slide

  39. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 39/126
    A load of bool
    https://puzzlemontage.crevado.com/

    View Slide

  40. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 40/126
    A load of bool
    void print(std::string_view, bool truncate, bool pad, bool line_feed);
    int main()
    {
            print("foo", false, false, true);
    }

    View Slide

  41. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 41/126
    A load of bool
    void print(std::string_view, bool truncate, bool pad, bool line_feed);
    int main()
    {
            print("foo", false, false, true);
    }
    Looks alright here

    View Slide

  42. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 42/126
    A load of bool
    void print(std::string_view, bool truncate, bool pad, bool line_feed);
    int main()
    {
            print("foo", false, false, true);
    }
    Looks alright here
    But completely
    incomprehensible here.
    And even if you use named
    variables, there’s no way to
    know if you have them in the
    right order.

    View Slide

  43. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 43/126
    A load of bool
    enum class truncate { off, on };
    enum class pad { off, on };
    enum class line_feed { off, on };
    void print(std::string_view, truncate truncate_, pad pad_, line_feed lf_);
    int main()
    {
            print("foo", truncate::off, pad::off, line_feed::on);
    }

    View Slide

  44. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 44/126
    A load of bool
    enum class truncate { off, on };
    enum class pad { off, on };
    enum class line_feed { off, on };
    void print(std::string_view, truncate truncate_, pad pad_, line_feed lf_);
    int main()
    {
            print("foo", truncate::off, pad::off, line_feed::on);
    }
    Enum class to
    the rescue!

    View Slide

  45. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 45/126
    A load of bool
    enum class truncate { off, on };
    enum class pad { off, on };
    enum class line_feed { off, on };
    void print(std::string_view, truncate truncate_, pad pad_, line_feed lf_);
    int main()
    {
            print("foo", truncate::off, pad::off, line_feed::on);
    }
    Enum class to
    the rescue!
    Very readable
    And each is a unique
    type, so reordering is a
    compilation error

    View Slide

  46. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 46/126
    A load of bool
    enum class truncate { off, on };
    enum class pad { off, on };
    enum class line_feed { off, on };
    void print(std::string_view, truncate truncate_, pad pad_, line_feed lf_);
    int main()
    {
            print("foo", truncate::off, pad::off, line_feed::on);
    }
    Enum class to
    the rescue!
    Very readable
    And each is a unique
    type, so reordering is a
    compilation error
    Although admittedly
    names of the
    parameters can be a bit
    hard on the eye

    View Slide

  47. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 47/126
    A load of bool

    You almost never want bool parameters, and
    especially not several
    ● enum class adds the good kind of verbosity
    that enhances readability

    View Slide

  48. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 48/126
    Dangerous defaults
    https://puzzlemontage.crevado.com/

    View Slide

  49. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 49/126
    Dangerous defaults
    void
    print(std::string_view, bool trunc, bool pad, bool lf=true);
    int main()
    {
            print("foo", false, false, true);
    }
    Same as before, but
    now we want line feed
    to be the default

    View Slide

  50. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 50/126
    Dangerous defaults
    void
    print(std::string_view, bool trunc, bool pad, bool lf=true);
    int main()
    {
            print("foo", false, false, true);
    }

    View Slide

  51. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 51/126
    Dangerous defaults
    void
    print(std::string_view, bool trunc, bool pad, bool lf=true);
    int main()
    {
            print("foo", false, false, true);
    }
    After some time we
    want to make the field
    size a parameter.

    View Slide

  52. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 52/126
    Dangerous defaults
    void
    print(std::string_view, size_t field, bool trunc, bool pad, bool lf=true);
    int main()
    {
            print("foo", false, false, true);
    }
    After some time we
    want to make the field
    size a parameter.

    View Slide

  53. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 53/126
    Dangerous defaults
    void
    print(std::string_view, size_t field, bool trunc, bool pad, bool lf=true);
    int main()
    {
            print("foo", false, false, true);
    }
    After some time we
    want to make the field
    size a parameter.
    ⚠️
    Still compiles, with
    field size == 0!!!

    View Slide

  54. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 54/126
    Dangerous defaults
    enum class truncate { off, on };
    enum class pad { off, on };
    enum class line_feed { off, on };
    void
    print(std::string_view,
    size_t field,
    truncate trunc_,
    pad pad_,
    line_feed lf_ = line_feed::on);
    int main()
    {
            print("foo", truncate::off, pad::off, line_feed::on);
    }

    View Slide

  55. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 55/126
    Dangerous defaults
    enum class truncate { off, on };
    enum class pad { off, on };
    enum class line_feed { off, on };
    void
    print(std::string_view,
    size_t field,
    truncate trunc_,
    pad pad_,
    line_feed lf_ = line_feed::on);
    int main()
    {
            print("foo", truncate::off, pad::off, line_feed::on);
    }
    note: initializing argument 2 of 'void print(std::string_view, size_t,
    truncate, pad, line_feed)'
    | void print(std::string_view, size_t field, truncate trunc_, pad pad_, line_feed lf_);
    | ~~~~~~~^~~~~
    ~~~~~~~~~~~~~

    View Slide

  56. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 56/126
    Dangerous defaults

    Default parameters are extremely dangerous
    over time if the parameter types are
    interchangeable

    Unique and non-convertible types catch this
    problem

    View Slide

  57. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 57/126
    Coupled parameters
    https://puzzlemontage.crevado.com/

    View Slide

  58. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 58/126
    Coupled parameters
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }

    View Slide

  59. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 59/126
    Coupled parameters
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }

    View Slide

  60. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 60/126
    Coupled parameters
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }

    View Slide

  61. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 61/126
    Coupled parameters
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }
    Oops! Do I get
    a CVE now?
    But what to do?

    View Slide

  62. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 62/126
    Coupled parameters
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }
    These parameters describe one
    thing, a contiguous range of
    memory that can be inspected.
    Let’s represent that in one type.

    View Slide

  63. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 63/126
    Coupled parameters
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }

    View Slide

  64. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 64/126
    Coupled parameters
    struct buffer_view {
    const uint8_t* const begin;
    const size_t length;
    buffer_view prefix(size_t len) const {
    return { begin, std::min(len, length)};
    }
    buffer_view suffix_after(size_t pos) const {
    auto adjusted_pos = std::min(pos, length);
    return { begin + adjusted_pos, length - adjusted_pos };
    }
    };
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }

    View Slide

  65. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 65/126
    Coupled parameters
    struct buffer_view {
    const uint8_t* const begin;
    const size_t length;
    buffer_view prefix(size_t len) const {
    return { begin, std::min(len, length)};
    }
    buffer_view suffix_after(size_t pos) const {
    auto adjusted_pos = std::min(pos, length);
    return { begin + adjusted_pos, length - adjusted_pos };
    }
    };
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }
    const public data

    View Slide

  66. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 66/126
    Coupled parameters
    struct buffer_view {
    const uint8_t* const begin;
    const size_t length;
    buffer_view prefix(size_t len) const {
    return { begin, std::min(len, length)};
    }
    buffer_view suffix_after(size_t pos) const {
    auto adjusted_pos = std::min(pos, length);
    return { begin + adjusted_pos, length - adjusted_pos };
    }
    };
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }
    Return new views
    Return new views

    View Slide

  67. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 67/126
    Coupled parameters
    struct buffer_view {
    const uint8_t* const begin;
    const size_t length;
    buffer_view prefix(size_t len) const {
    return { begin, std::min(len, length)};
    }
    buffer_view suffix_after(size_t pos) const {
    auto adjusted_pos = std::min(pos, length);
    return { begin + adjusted_pos, length - adjusted_pos };
    }
    };
    size_t parse_header(const uint8_t* buffer, size_t buffer_length);
    void copy_payload(const uint8_t* begin, size_t payload_length);
    void received_packet(const uint8_t* buffer, size_t buffer_length)
    {
    auto header_len = parse_header(buffer, buffer_length);
    copy_payload(buffer + header_len, buffer_length);
    }
    Truncate if necessary

    View Slide

  68. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 68/126
    Coupled parameters
    buffer_view parse_header(buffer_view packet);
    void copy_payload(buffer_view payload);
    void received_packet(buffer_view packet)
    {
    auto header = parse_header(packet);
    copy_payload(packet.suffix_after(header.length));
    }
    struct buffer_view {
    const uint8_t* const begin;
    const size_t length;
    buffer_view prefix(size_t len) const {
    return { begin, std::min(len, length)};
    }
    buffer_view suffix_after(size_t pos) const {
    auto adjusted_pos = std::min(pos, length);
    return { begin + adjusted_pos, length - adjusted_pos };
    }
    };

    View Slide

  69. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 69/126
    Clearer intent and
    reduced risk of error
    Coupled parameters
    buffer_view parse_header(buffer_view packet);
    void copy_payload(buffer_view payload);
    void received_packet(buffer_view packet)
    {
    auto header = parse_header(packet);
    copy_payload(packet.suffix_after(header.length));
    }
    struct buffer_view {
    const uint8_t* const begin;
    const size_t length;
    buffer_view prefix(size_t len) const {
    return { begin, std::min(len, length)};
    }
    buffer_view suffix_after(size_t pos) const {
    auto adjusted_pos = std::min(pos, length);
    return { begin + adjusted_pos, length - adjusted_pos };
    }
    };

    View Slide

  70. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 70/126
    Clearer intent and
    reduced risk of error
    Coupled parameters
    buffer_view parse_header(buffer_view packet);
    void copy_payload(buffer_view payload);
    void received_packet(buffer_view packet)
    {
    auto header = parse_header(packet);
    copy_payload(packet.suffix_after(header.length));
    }
    struct buffer_view {
    const uint8_t* const begin;
    const size_t length;
    buffer_view prefix(size_t len) const {
    return { begin, std::min(len, length)};
    }
    buffer_view suffix_after(size_t pos) const {
    auto adjusted_pos = std::min(pos, length);
    return { begin + adjusted_pos, length - adjusted_pos };
    }
    };
    But immutable
    members can
    be problematic

    View Slide

  71. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 71/126
    Coupled parameters
    class buffer_view {
    const uint8_t* start;
    size_t length;
    public:
    buffer_view(const uint8_t* buff, size_t len)
    : start(buff), length(len) {}
    const uint8_t* begin() const { return start; }
    const uint8_t* end() const { return start + length; }
    size_t size() const { return length;}
    buffer_view prefix(size_t len) const;
    buffer_view suffix_after(size_t pos) const;
    };

    View Slide

  72. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 72/126
    Coupled parameters
    class buffer_view {
    const uint8_t* start;
    size_t length;
    public:
    buffer_view(const uint8_t* buff, size_t len)
    : start(buff), length(len) {}
    const uint8_t* begin() const { return start; }
    const uint8_t* end() const { return start + length; }
    size_t size() const { return length;}
    buffer_view prefix(size_t len) const;
    buffer_view suffix_after(size_t pos) const;
    };
    mutable private data

    View Slide

  73. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 73/126
    Coupled parameters
    class buffer_view {
    const uint8_t* start;
    size_t length;
    public:
    buffer_view(const uint8_t* buff, size_t len)
    : start(buff), length(len) {}
    const uint8_t* begin() const { return start; }
    const uint8_t* end() const { return start + length; }
    size_t size() const { return length;}
    buffer_view prefix(size_t len) const;
    buffer_view suffix_after(size_t pos) const;
    };
    Access functions that
    play nicely with range-for

    View Slide

  74. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 74/126
    Coupled parameters
    class buffer_view {
    const uint8_t* start;
    size_t length;
    public:
    buffer_view(const uint8_t* buff, size_t len)
    : start(buff), length(len) {}
    const uint8_t* begin() const { return start; }
    const uint8_t* end() const { return start + length; }
    size_t size() const { return length;}
    buffer_view prefix(size_t len) const;
    buffer_view suffix_after(size_t pos) const;
    };
    Resist the
    temptation to
    add too much!

    View Slide

  75. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 75/126
    Coupled parameters
    class buffer_view {
    const uint8_t* start;
    size_t length;
    public:
    buffer_view(const uint8_t* buff, size_t len)
    : start(buff), length(len) {}
    const uint8_t* begin() const { return start; }
    const uint8_t* end() const { return start + length; }
    size_t size() const { return length;}
    buffer_view prefix(size_t len) const;
    buffer_view suffix_after(size_t pos) const;
    };
    Resist the
    temptation to
    add too much!

    View Slide

  76. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 76/126
    Coupled parameters
    class buffer_view {
    const uint8_t* start;
    size_t length;
    public:
    buffer_view(const uint8_t* buff, size_t len)
    : start(buff), length(len) {}
    const uint8_t* begin() const { return start; }
    const uint8_t* end() const { return start + length; }
    size_t size() const { return length;}
    buffer_view prefix(size_t len) const;
    buffer_view suffix_after(size_t pos) const;
    };
    Resist the
    temptation to
    add too much!
    Go back and
    add stuff later
    if needed

    View Slide

  77. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 77/126
    Coupled parameters

    When several parameters together describe
    one thing, model that one thing as a type

    C++20 has std::span<> to model the
    start+length view into a buffer.

    View Slide

  78. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 78/126
    Too many defaults
    https://puzzlemontage.crevado.com/

    View Slide

  79. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 79/126
    Too many defaults
    class server_socket {
    public:
    server_socket(uint16_t port,
    bool tcp = true,
    std::string_view address="0.0.0.0",
    std::optional multicast = std::nullopt,
    bool nonblocking = true);

    };
    What if I want all the
    defaults, but
    nonblocking = false?

    View Slide

  80. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 80/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };

    View Slide

  81. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 81/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    Use member initializers
    for the defaults

    View Slide

  82. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 82/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }

    View Slide

  83. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 83/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    C++20 designated
    initializer list for explicit
    named values

    View Slide

  84. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 84/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    C++20 designated
    initializer list for explicit
    named values
    server_socket::config conf;
    conf.port = 1666;
    conf.address = "127.0.0.1";
    conf.nonblocking = false;
    server_socket socket(conf);
    Before C++20

    View Slide

  85. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 85/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    C++20 designated
    initializer list for explicit
    named values
    server_socket::config conf;
    conf.port = 1666;
    conf.address = "127.0.0.1";
    conf.nonblocking = false;
    server_socket socket(conf);
    Before C++20
    ⚠️
    Omitting .port
    is not an error!

    View Slide

  86. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 86/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    template
    class must_init {
    public:
    must_init(T t) : value(std::move(t)) {}
    operator T&() { return value; }
    operator const T&() const { return value; }
    private:
    T value;
    };

    View Slide

  87. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 87/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    template
    class must_init {
    public:
    must_init(T t) : value(std::move(t)) {}
    operator T&() { return value; }
    operator const T&() const { return value; }
    private:
    T value;
    };
    No default constructor!

    View Slide

  88. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 88/126
    Too many defaults
    class server_socket {
    public:
    struct config {
    uint16_t port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    template
    class must_init {
    public:
    must_init(T t) : value(std::move(t)) {}
    operator T&() { return value; }
    operator const T&() const { return value; }
    private:
    T value;
    };
    Access underlying value

    View Slide

  89. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 89/126
    Too many defaults
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    class server_socket {
    public:
    struct config {
    must_init port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    template
    class must_init {
    public:
    must_init(T t) : value(std::move(t)) {}
    operator T&() { return value; }
    operator const T&() const { return value; }
    private:
    T value;
    };

    View Slide

  90. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 90/126
    Failure to init is now
    a compilation error!
    Too many defaults
    int main()
    {
    server_socket socket({.port = 1666,
    .address = "127.0.0.1",
    .nonblocking = false});

    }
    class server_socket {
    public:
    struct config {
    must_init port;
    bool tcp = true;
    std::string_view address = "0.0.0.0";
    std::optional multicast = std::nullopt;
    bool nonblocking = true;
    };
    server_socket(config);

    };
    template
    class must_init {
    public:
    must_init(T t) : value(std::move(t)) {}
    operator T&() { return value; }
    operator const T&() const { return value; }
    private:
    T value;
    };

    View Slide

  91. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 91/126
    Too many defaults

    Creating structs with parameters is especially
    helpful when many of them have reasonable
    defaults

    The technique is extra useful since C++20
    thanks to the designated initializer syntax

    View Slide

  92. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 92/126
    Wrong key
    https://puzzlemontage.crevado.com/

    View Slide

  93. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 93/126
    Wrong key
    using client_id = int;
    using server_id = int;
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  94. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 94/126
    Wrong key
    using client_id = int;
    using server_id = int;
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  95. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 95/126
    Wrong key
    using client_id = int;
    using server_id = int;
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  96. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 96/126
    Wrong key
    using client_id = int;
    using server_id = int;
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };
    Oops!
    But what to
    do?

    View Slide

  97. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 97/126
    Wrong key
    using client_id = int;
    using server_id = int;
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };
    A type alias is
    little more than
    a comment!

    View Slide

  98. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 98/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  99. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 99/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };
    Creates new unique
    types and works for
    any integer type

    View Slide

  100. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 100/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };
    Creates new unique
    types and works for
    any integer type
    C++17 and
    later

    View Slide

  101. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 101/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_client_session.erase(id);
    }
    void close_client(client_id id);
    };
    error: no matching function for call to 'std::map::erase(server_id&)'
    m_client_session.erase(id);
    ~~~~~~~~~~~~~~~~~~~~~~~~~~

    View Slide

  102. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 102/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  103. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 103/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;

    View Slide

  104. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 104/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;
    Create from underlying type

    View Slide

  105. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 105/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;
    Get value from underlying type

    View Slide

  106. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 106/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;
    Get value from underlying type
    C++23 has
    std::to_underlying(y)

    View Slide

  107. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 107/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;
    equality and ordering
    comparisons

    View Slide

  108. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 108/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  109. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 109/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  110. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 110/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  111. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 111/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  112. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 112/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };

    View Slide

  113. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 113/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_server(server_id id) {
    m_server_session.erase(id);
    }
    void close_client(client_id id);
    };
    Redundant information
    in the name. The type
    carries the intent.

    View Slide

  114. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 114/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };

    View Slide

  115. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 115/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    We can overload
    functions on
    different parameter
    types

    View Slide

  116. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 116/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    We can overload
    functions on
    different parameter
    types

    View Slide

  117. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 117/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    What if the
    keys aren’t of
    integer types?

    View Slide

  118. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 118/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    What if the
    keys aren’t of
    integer types?
    Can’t use std::string as
    underlying type for an enum

    View Slide

  119. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 119/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    #include
    #include
    class server_id {
            std::string value;
    public:
            explicit server_id(std::string id) : value(std::move(id)) {}
            explicit operator std::string_view() const { return value; }
            auto operator<=>(const server_id&) const = default;
    };

    View Slide

  120. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 120/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    #include
    #include
    class server_id {
            std::string value;
    public:
            explicit server_id(std::string id) : value(std::move(id)) {}
            explicit operator std::string_view() const { return value; }
            auto operator<=>(const server_id&) const = default;
    };
    Always use an explicit constructor

    View Slide

  121. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 121/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    #include
    #include
    class server_id {
            std::string value;
    public:
            explicit server_id(std::string id) : value(std::move(id)) {}
            explicit operator std::string_view() const { return value; }
            auto operator<=>(const server_id&) const = default;
    };
    and an explicit conversion operator

    View Slide

  122. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 122/126
    Wrong key
    enum class client_id : int {};
    enum class server_id : int {};
    class control
    {
    std::map m_client_session;
    std::map m_server_session;
    public:
    void close_session(server_id id) {
    m_server_session.erase(id);
    }
    void close_session(client_id id);
    };
    #include
    #include
    class server_id {
            std::string value;
    public:
            explicit server_id(std::string id) : value(std::move(id)) {}
            explicit operator std::string_view() const { return value; }
            auto operator<=>(const server_id&) const = default;
    };
    C++20 magic comparison operators

    View Slide

  123. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 123/126
    Wrong key
    ● enum class is great for creating new integer
    types

    C++20 operator<=>() saves a lot of work

    View Slide

  124. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 124/126
    Types
    https://puzzlemontage.crevado.com/

    Reduce the risk of calling functions with the wrong values

    Reduce the risk that dependent values diverge

    Makes it easier to manage defaults

    Can help prevent uninitialized data

    Are more convenient to write and use since C++20

    Make your APIs more expressive

    Are typically discovered as the code evolves

    Just don’t forget to incorporate the improvements

    View Slide

  125. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 125/126
    Furthermore...
    The amazing art work used in this presentation
    https://puzzlemontage.crevado.com/
    https://youtu.be/iLpt23V2vQE
    Theory and details to think
    about when designing types
    The benefits and
    traps when making
    types for containers
    https://youtu.be/0cTOqwrvq94

    View Slide

  126. Typical C++, But Why? – CppN
    🍁
    rth 2023 © Björn Fahller @[email protected] 126/126
    Björn Fahller
    Typical C++, But Why?
    [email protected]
    @[email protected]
    @rollbear
    Björn Fahller

    View Slide