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

NDC{TechTown} : Typical C++, but Why?

NDC{TechTown} : 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

August 31, 2022
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 1/124
    Typical C++, But Why?
    Björn Fahller
    https://puzzlemontage.crevado.com/

    View Slide

  2. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 2/124
    Typical C++, But Why?

    View Slide

  3. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 3/124
    Typical C++, But Why?

    View Slide

  4. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 4/124
    Typical C++, But Why?

    View Slide

  5. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 5/124
    Typical C++, But Why?

    View Slide

  6. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 6/124
    Typical C++, But Why?
    Björn Fahller
    https://puzzlemontage.crevado.com/

    View Slide

  7. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 7/124
    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

  8. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 8/124
    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

  9. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 9/124
    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

  10. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 10/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 11/124
    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

  12. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 12/124
    Wrong argument
    https://puzzlemontage.crevado.com/

    View Slide

  13. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 13/124
    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

  14. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 14/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 15/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 16/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 17/124
    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

  18. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 18/124
    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

  19. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 19/124
    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

  20. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 20/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 21/124
    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

  22. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 22/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 23/124
    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

  24. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 24/124
    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

  25. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 25/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 26/124
    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

  27. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 27/124
    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

  28. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 28/124
    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

  29. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 29/124
    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

  30. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 30/124
    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

  31. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 31/124
    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

  32. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 32/124
    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

  33. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 33/124
    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

  34. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 34/124
    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

  35. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 35/124
    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

  36. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 36/124
    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

  37. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 37/124
    Wrong argument

    Even a very simple struct eliminates a whole
    class of 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

  38. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 38/124
    A load of bool
    https://puzzlemontage.crevado.com/

    View Slide

  39. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 39/124
    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

  40. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 40/124
    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

  41. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 41/124
    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

  42. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 42/124
    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

  43. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 43/124
    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

  44. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 44/124
    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

  45. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 45/124
    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

  46. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 46/124
    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

  47. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 47/124
    Dangerous defaults
    https://puzzlemontage.crevado.com/

    View Slide

  48. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 48/124
    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

  49. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 49/124
    Dangerous defaults
    void
    print(std::string_view, bool trunc, bool pad, bool lf=true);
    int main()
    {
            print("foo", false, false, true);
    }

    View Slide

  50. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 50/124
    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

  51. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 51/124
    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

  52. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 52/124
    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

  53. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 53/124
    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

  54. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 54/124
    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

  55. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 55/124
    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

  56. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 56/124
    Coupled parameters
    https://puzzlemontage.crevado.com/

    View Slide

  57. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 57/124
    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

  58. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 58/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 59/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 60/124
    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

  61. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 61/124
    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

  62. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 62/124
    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

  63. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 63/124
    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

  64. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 64/124
    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

  65. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 65/124
    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

  66. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 66/124
    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

  67. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 67/124
    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

  68. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 68/124
    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

  69. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 69/124
    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

  70. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 70/124
    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

  71. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 71/124
    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

  72. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 72/124
    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

  73. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 73/124
    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

  74. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 74/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 75/124
    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

  76. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 76/124
    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

  77. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 77/124
    Too many defaults
    https://puzzlemontage.crevado.com/

    View Slide

  78. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 78/124
    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

  79. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 79/124
    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

  80. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 80/124
    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

  81. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 81/124
    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

  82. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 82/124
    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

  83. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 83/124
    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

  84. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 84/124
    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

  85. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 85/124
    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

  86. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 86/124
    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

  87. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 87/124
    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

  88. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 88/124
    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

  89. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 89/124
    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

  90. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 90/124
    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

  91. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 91/124
    Wrong key
    https://puzzlemontage.crevado.com/

    View Slide

  92. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 92/124
    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

  93. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 93/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 94/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 95/124
    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

  96. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 96/124
    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

  97. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 97/124
    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

  98. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 98/124
    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

  99. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 99/124
    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

  100. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 100/124
    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

  101. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 101/124
    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

  102. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 102/124
    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? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 103/124
    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

  104. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 104/124
    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

  105. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 105/124
    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

  106. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 106/124
    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

  107. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 107/124
    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

  108. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 108/124
    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

  109. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 109/124
    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

  110. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 110/124
    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

  111. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 111/124
    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);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;

    View Slide

  112. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 112/124
    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);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;
    Create from underlying type

    View Slide

  113. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 113/124
    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);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;
    Get value from underlying type

    View Slide

  114. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 114/124
    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);
    };
    auto x = client_id{3};
    auto y = x;
    int v = static_cast(y);
    bool b = x < y;
    equality and ordering
    comparisons

    View Slide

  115. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 115/124
    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

  116. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 116/124
    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

  117. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 117/124
    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

  118. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 118/124
    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

  119. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 119/124
    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

  120. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 120/124
    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

  121. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 121/124
    Wrong key

    enum class is great for creating new integer
    types

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

    View Slide

  122. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 122/124
    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

  123. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 123/124
    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
    Premiers on Sept 18!

    View Slide

  124. Typical C++, But Why? – NDC{TechTown} 2022 © Björn Fahller @bjorn_fahller 124/124
    [email protected]
    @bjorn_fahller
    @rollbear
    Björn Fahller
    Typical C++, But Why?

    View Slide