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] 2/126 Typical C++, But Why? Björn Fahller https://puzzlemontage.crevado.com/
  2. Typical C++, But Why? – CppN 🍁 rth 2023 ©

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

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

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

    Björn Fahller @[email protected] 6/126 Typical C++, But Why?
  6. 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/
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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.
  12. Typical C++, But Why? – CppN 🍁 rth 2023 ©

    Björn Fahller @[email protected] 13/126 Wrong argument https://puzzlemontage.crevado.com/
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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/
  18. 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?
  19. 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); }
  20. 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); }
  21. 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; };
  22. 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; };
  23. 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<char>' | auto result = query_user_data(input); | ^~~~~ ~~~~~~~~~~~~~~~~~~~~~~ struct sanitized_string { std::string value; };
  24. 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; };
  25. 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; };
  26. 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); }
  27. 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.
  28. 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.
  29. 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<int>(c)); }
  30. 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<int>(c)); } Making the conversion operator explicit eliminates the risk for accidental conversions.
  31. 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<int>(c)); } Making the conversion operator explicit eliminates the risk for accidental conversions. You have to say you want a conversion.
  32. 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; };
  33. 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
  34. 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
  35. 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.
  36. 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
  37. 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
  38. Typical C++, But Why? – CppN 🍁 rth 2023 ©

    Björn Fahller @[email protected] 39/126 A load of bool https://puzzlemontage.crevado.com/
  39. 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); }
  40. 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
  41. 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.
  42. 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); }
  43. 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!
  44. 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
  45. 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
  46. 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
  47. Typical C++, But Why? – CppN 🍁 rth 2023 ©

    Björn Fahller @[email protected] 48/126 Dangerous defaults https://puzzlemontage.crevado.com/
  48. 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
  49. 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); }
  50. 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.
  51. 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.
  52. 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!!!
  53. 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); }
  54. 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_); | ~~~~~~~^~~~~ ~~~~~~~~~~~~~
  55. 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
  56. Typical C++, But Why? – CppN 🍁 rth 2023 ©

    Björn Fahller @[email protected] 57/126 Coupled parameters https://puzzlemontage.crevado.com/
  57. 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); }
  58. 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); }
  59. 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); }
  60. 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?
  61. 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.
  62. 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); }
  63. 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); }
  64. 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
  65. 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
  66. 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
  67. 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 }; } };
  68. 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 }; } };
  69. 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
  70. 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; };
  71. 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
  72. 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
  73. 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!
  74. 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!
  75. 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
  76. 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.
  77. Typical C++, But Why? – CppN 🍁 rth 2023 ©

    Björn Fahller @[email protected] 78/126 Too many defaults https://puzzlemontage.crevado.com/
  78. 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<std::string_view> multicast = std::nullopt, bool nonblocking = true); … }; What if I want all the defaults, but nonblocking = false?
  79. 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<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … };
  80. 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<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … }; Use member initializers for the defaults
  81. 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<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … }; int main() { server_socket socket({.port = 1666, .address = "127.0.0.1", .nonblocking = false}); … }
  82. 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<std::string_view> 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
  83. 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<std::string_view> 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
  84. 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<std::string_view> 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!
  85. 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<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … }; int main() { server_socket socket({.port = 1666, .address = "127.0.0.1", .nonblocking = false}); … } template <typename T> 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; };
  86. 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<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … }; int main() { server_socket socket({.port = 1666, .address = "127.0.0.1", .nonblocking = false}); … } template <typename T> 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!
  87. 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<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … }; int main() { server_socket socket({.port = 1666, .address = "127.0.0.1", .nonblocking = false}); … } template <typename T> 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
  88. 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<uint16_t> port; bool tcp = true; std::string_view address = "0.0.0.0"; std::optional<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … }; template <typename T> 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; };
  89. 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<uint16_t> port; bool tcp = true; std::string_view address = "0.0.0.0"; std::optional<std::string_view> multicast = std::nullopt; bool nonblocking = true; }; server_socket(config); … }; template <typename T> 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; };
  90. 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
  91. Typical C++, But Why? – CppN 🍁 rth 2023 ©

    Björn Fahller @[email protected] 92/126 Wrong key https://puzzlemontage.crevado.com/
  92. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_client_session.erase(id); } void close_client(client_id id); };
  93. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_client_session.erase(id); } void close_client(client_id id); };
  94. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_client_session.erase(id); } void close_client(client_id id); };
  95. 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<client_id, session> m_client_session; std::map<server_id, session> 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?
  96. 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<client_id, session> m_client_session; std::map<server_id, session> 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!
  97. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_client_session.erase(id); } void close_client(client_id id); };
  98. 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<client_id, session> m_client_session; std::map<server_id, session> 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
  99. 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<client_id, session> m_client_session; std::map<server_id, session> 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
  100. 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<client_id, session> m_client_session; std::map<server_id, session> 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<client_id, session>::erase(server_id&)' m_client_session.erase(id); ~~~~~~~~~~~~~~~~~~~~~~~~~~
  101. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_server_session.erase(id); } void close_client(client_id id); };
  102. 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<client_id, session> m_client_session; std::map<server_id, session> 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<int>(y); bool b = x < y;
  103. 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<client_id, session> m_client_session; std::map<server_id, session> 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<int>(y); bool b = x < y; Create from underlying type
  104. 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<client_id, session> m_client_session; std::map<server_id, session> 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<int>(y); bool b = x < y; Get value from underlying type
  105. 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<client_id, session> m_client_session; std::map<server_id, session> 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<int>(y); bool b = x < y; Get value from underlying type C++23 has std::to_underlying(y)
  106. 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<client_id, session> m_client_session; std::map<server_id, session> 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<int>(y); bool b = x < y; equality and ordering comparisons
  107. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_server_session.erase(id); } void close_client(client_id id); };
  108. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_server_session.erase(id); } void close_client(client_id id); };
  109. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_server_session.erase(id); } void close_client(client_id id); };
  110. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_server_session.erase(id); } void close_client(client_id id); };
  111. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_server(server_id id) { m_server_session.erase(id); } void close_client(client_id id); };
  112. 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<client_id, session> m_client_session; std::map<server_id, session> 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.
  113. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_session(server_id id) { m_server_session.erase(id); } void close_session(client_id id); };
  114. 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<client_id, session> m_client_session; std::map<server_id, session> 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
  115. 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<client_id, session> m_client_session; std::map<server_id, session> 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
  116. 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<client_id, session> m_client_session; std::map<server_id, session> 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?
  117. 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<client_id, session> m_client_session; std::map<server_id, session> 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
  118. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_session(server_id id) { m_server_session.erase(id); } void close_session(client_id id); }; #include <compare> #include <string> 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; };
  119. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_session(server_id id) { m_server_session.erase(id); } void close_session(client_id id); }; #include <compare> #include <string> 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
  120. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_session(server_id id) { m_server_session.erase(id); } void close_session(client_id id); }; #include <compare> #include <string> 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
  121. 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<client_id, session> m_client_session; std::map<server_id, session> m_server_session; public: void close_session(server_id id) { m_server_session.erase(id); } void close_session(client_id id); }; #include <compare> #include <string> 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
  122. 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
  123. 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
  124. 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
  125. 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