Slide 1

Slide 1 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 1/125 Typical C++, But Why? Björn Fahller https://puzzlemontage.crevado.com/

Slide 2

Slide 2 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 2/125 Typical C++, But Why?

Slide 3

Slide 3 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 3/125 Typical C++, But Why?

Slide 4

Slide 4 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 4/125 Typical C++, But Why?

Slide 5

Slide 5 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 5/125 Typical C++, But Why?

Slide 6

Slide 6 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 6/125 Typical C++, But Why? Björn Fahller https://puzzlemontage.crevado.com/

Slide 7

Slide 7 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 7/125 C++ type system in a nutshell ● C++ has a small standard set of jigsaw puzzle piece shapes, which often fit in the wrong places

Slide 8

Slide 8 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 8/125 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

Slide 9

Slide 9 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 9/125 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

Slide 10

Slide 10 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 10/125 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

Slide 11

Slide 11 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 11/125 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.

Slide 12

Slide 12 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 12/125 Wrong argument https://puzzlemontage.crevado.com/

Slide 13

Slide 13 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 13/125 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

Slide 14

Slide 14 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 14/125 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

Slide 15

Slide 15 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 15/125 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

Slide 16

Slide 16 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 16/125 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

Slide 17

Slide 17 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 17/125 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/

Slide 18

Slide 18 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 18/125 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?

Slide 19

Slide 19 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 19/125 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); }

Slide 20

Slide 20 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 20/125 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); }

Slide 21

Slide 21 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 21/125 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; };

Slide 22

Slide 22 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 22/125 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; };

Slide 23

Slide 23 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 23/125 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; };

Slide 24

Slide 24 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 24/125 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; };

Slide 25

Slide 25 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 25/125 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; };

Slide 26

Slide 26 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 26/125 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); }

Slide 27

Slide 27 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 27/125 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.

Slide 28

Slide 28 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 28/125 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.

Slide 29

Slide 29 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 29/125 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)); }

Slide 30

Slide 30 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 30/125 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.

Slide 31

Slide 31 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 31/125 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.

Slide 32

Slide 32 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 32/125 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; };

Slide 33

Slide 33 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 33/125 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

Slide 34

Slide 34 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 34/125 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

Slide 35

Slide 35 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 35/125 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.

Slide 36

Slide 36 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 36/125 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

Slide 37

Slide 37 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 37/125 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

Slide 38

Slide 38 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 38/125 A load of bool https://puzzlemontage.crevado.com/

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 40/125 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

Slide 41

Slide 41 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 41/125 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.

Slide 42

Slide 42 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 42/125 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); }

Slide 43

Slide 43 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 43/125 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!

Slide 44

Slide 44 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 44/125 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

Slide 45

Slide 45 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 45/125 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

Slide 46

Slide 46 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 46/125 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

Slide 47

Slide 47 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 47/125 Dangerous defaults https://puzzlemontage.crevado.com/

Slide 48

Slide 48 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 48/125 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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 50/125 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.

Slide 51

Slide 51 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 51/125 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.

Slide 52

Slide 52 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 52/125 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!!!

Slide 53

Slide 53 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 53/125 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); }

Slide 54

Slide 54 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 54/125 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_); | ~~~~~~~^~~~~ ~~~~~~~~~~~~~

Slide 55

Slide 55 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 55/125 Dangerous defaults ● Default parameters are extremely dangerous over time if the parameter types are interchangeable ● Unique and non-convertible types catch this problem

Slide 56

Slide 56 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 56/125 Coupled parameters https://puzzlemontage.crevado.com/

Slide 57

Slide 57 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 57/125 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); }

Slide 58

Slide 58 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 58/125 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); }

Slide 59

Slide 59 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 59/125 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); }

Slide 60

Slide 60 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 60/125 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?

Slide 61

Slide 61 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 61/125 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.

Slide 62

Slide 62 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 62/125 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); }

Slide 63

Slide 63 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 63/125 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); }

Slide 64

Slide 64 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 64/125 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

Slide 65

Slide 65 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 65/125 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

Slide 66

Slide 66 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 66/125 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

Slide 67

Slide 67 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 67/125 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 }; } };

Slide 68

Slide 68 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 68/125 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 }; } };

Slide 69

Slide 69 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 69/125 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

Slide 70

Slide 70 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 70/125 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; };

Slide 71

Slide 71 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 71/125 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

Slide 72

Slide 72 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 72/125 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

Slide 73

Slide 73 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 73/125 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!

Slide 74

Slide 74 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 74/125 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!

Slide 75

Slide 75 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 75/125 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

Slide 76

Slide 76 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 76/125 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.

Slide 77

Slide 77 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 77/125 Too many defaults https://puzzlemontage.crevado.com/

Slide 78

Slide 78 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 78/125 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?

Slide 79

Slide 79 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 79/125 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); … };

Slide 80

Slide 80 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 80/125 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

Slide 81

Slide 81 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 81/125 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}); … }

Slide 82

Slide 82 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 82/125 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

Slide 83

Slide 83 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 83/125 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

Slide 84

Slide 84 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 84/125 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!

Slide 85

Slide 85 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 85/125 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; };

Slide 86

Slide 86 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 86/125 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!

Slide 87

Slide 87 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 87/125 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

Slide 88

Slide 88 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 88/125 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; };

Slide 89

Slide 89 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 89/125 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; };

Slide 90

Slide 90 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 90/125 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

Slide 91

Slide 91 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 91/125 Wrong key https://puzzlemontage.crevado.com/

Slide 92

Slide 92 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 92/125 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); };

Slide 93

Slide 93 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 93/125 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); };

Slide 94

Slide 94 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 94/125 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); };

Slide 95

Slide 95 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 95/125 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?

Slide 96

Slide 96 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 96/125 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!

Slide 97

Slide 97 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 97/125 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); };

Slide 98

Slide 98 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 98/125 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

Slide 99

Slide 99 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 99/125 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

Slide 100

Slide 100 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 100/125 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); ~~~~~~~~~~~~~~~~~~~~~~~~~~

Slide 101

Slide 101 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 101/125 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); };

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 107/125 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); };

Slide 108

Slide 108 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 108/125 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); };

Slide 109

Slide 109 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 109/125 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); };

Slide 110

Slide 110 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 110/125 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); };

Slide 111

Slide 111 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 111/125 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); };

Slide 112

Slide 112 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 112/125 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.

Slide 113

Slide 113 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 113/125 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); };

Slide 114

Slide 114 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 114/125 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

Slide 115

Slide 115 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 115/125 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

Slide 116

Slide 116 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 116/125 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?

Slide 117

Slide 117 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 117/125 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

Slide 118

Slide 118 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 118/125 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; };

Slide 119

Slide 119 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 119/125 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

Slide 120

Slide 120 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 120/125 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

Slide 121

Slide 121 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 121/125 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

Slide 122

Slide 122 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 122/125 Wrong key ● enum class is great for creating new integer types ● C++20 operator<=>() saves a lot of work ● Avoid the use of type aliases in function signatures

Slide 123

Slide 123 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 123/125 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

Slide 124

Slide 124 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 124/125 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

Slide 125

Slide 125 text

Typical C++, But Why? – MeetingC++ 2023 © Björn Fahller @[email protected] 125/125 Björn Fahller Typical C++, But Why? [email protected] @[email protected] @rollbear Björn Fahller