├── olt_cpp17 ├── 1_qol.pdf ├── 6_meta.pdf ├── 3_variant.pdf ├── 7_a_match.pdf ├── 4_optional.pdf ├── 5_sbindings.pdf ├── 2_attributes.pdf ├── 8_a_recursive_variant.pdf ├── solutions │ ├── exercise4_solution.cpp │ ├── exercise3_solution.cpp │ ├── exercise0_solution.cpp │ ├── exercise2_solution.cpp │ └── exercise1_solution.cpp ├── exercise4.cpp ├── exercise3.cpp ├── exercise2.cpp ├── exercise0.cpp └── exercise1.cpp ├── README.md └── .gitignore /olt_cpp17/1_qol.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/1_qol.pdf -------------------------------------------------------------------------------- /olt_cpp17/6_meta.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/6_meta.pdf -------------------------------------------------------------------------------- /olt_cpp17/3_variant.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/3_variant.pdf -------------------------------------------------------------------------------- /olt_cpp17/7_a_match.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/7_a_match.pdf -------------------------------------------------------------------------------- /olt_cpp17/4_optional.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/4_optional.pdf -------------------------------------------------------------------------------- /olt_cpp17/5_sbindings.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/5_sbindings.pdf -------------------------------------------------------------------------------- /olt_cpp17/2_attributes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/2_attributes.pdf -------------------------------------------------------------------------------- /olt_cpp17/8_a_recursive_variant.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioromeo/oreilly/HEAD/olt_cpp17/8_a_recursive_variant.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # oreilly 2 | 3 | Material and work for O'Reilly courses and publications 4 | 5 | ## Live Online Training 6 | 7 | ### What's new in C++ 8 | 9 | > **Comprehensive overview of C++17's killer features** 10 | 11 | * [Course Page](https://www.oreilly.com/live-training/courses/whats-new-in-c/0636920380245/) 12 | 13 | * [Slides and Exercises](https://github.com/SuperV1234/oreilly/blob/master/olt_cpp17) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /olt_cpp17/solutions/exercise4_solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct type_wrapper { using type = T; }; 6 | 7 | template 8 | void for_types(F&& f) 9 | { 10 | (f(type_wrapper{}), ...); 11 | } 12 | 13 | int main() 14 | { 15 | for_types([](auto t) 16 | { 17 | using T = typename decltype(t)::type; 18 | std::printf("sizeof(%s) = %ld\n", typeid(T).name(), sizeof(T)); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /olt_cpp17/exercise4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | void for_types(F&& f) 6 | { 7 | // TODO: implement this function. 8 | } 9 | 10 | int main() 11 | { 12 | for_types([](auto t) 13 | { 14 | using T = typename decltype(t)::type; 15 | std::printf("sizeof(%s) = %ld\n", typeid(T).name(), sizeof(T)); 16 | }); 17 | 18 | // TODO: should print: 19 | /* 20 | sizeof(c) = 1 21 | sizeof(i) = 4 22 | sizeof(f) = 4 23 | sizeof(d) = 8 24 | */ 25 | } 26 | -------------------------------------------------------------------------------- /olt_cpp17/exercise3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct vec3 5 | { 6 | private: 7 | T _x, _y, _z; 8 | 9 | public: 10 | vec3(T x, T y, T z) 11 | : _x{x}, _y{y}, _z{z} 12 | { 13 | } 14 | 15 | T& x() { return _x; } 16 | const T& x() const { return _x; } 17 | 18 | T& y() { return _y; } 19 | const T& y() const { return _y; } 20 | 21 | T& z() { return _z; } 22 | const T& z() const { return _z; } 23 | }; 24 | 25 | int main() 26 | { 27 | const vec3 point{2.f, 1.f, 4.f}; 28 | const vec3 scaling_factors{2.f, 4.f, 10.f}; 29 | 30 | // TODO: make the following code compile. 31 | const auto& [px, py, pz] = point; 32 | const auto& [sx, sy, sz] = scaling_factors; 33 | 34 | const vec3 result{px * sx, py * sy, pz * sz}; 35 | 36 | std::printf("%f %f %f\n", 37 | result.x(), result.y(), result.z()); 38 | } 39 | -------------------------------------------------------------------------------- /olt_cpp17/exercise2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Imagine a networked application where messages are exchanged between clients 6 | // and a server. Your goal is to model the messaged described below in a type 7 | // safe manner, using algebraic data types (`std::variant`, `std::optional`). 8 | 9 | // * A client can attempt to connect to a server. The server will reply with 10 | // either success or failure. 11 | 12 | // * A client can attempt to create a new key on the server. Handle possible 13 | // failure cases. 14 | 15 | // * A client can attempt to set a value for a key-value pair on the server. 16 | // Handle possible failure cases. 17 | 18 | // * A client can attempt to read a value from a key-value pair on the server. 19 | 20 | struct client_message 21 | { 22 | // TODO: define your data members here. 23 | }; 24 | 25 | struct server_message 26 | { 27 | // TODO: define your data members here. 28 | }; 29 | 30 | struct client 31 | { 32 | void send(const client_message& message, const char* server_ip) 33 | { 34 | // DO NOT IMPLEMENT. This is just to help you understand how the client 35 | // and the server communicate. 36 | 37 | (void)message; 38 | (void)server_ip; 39 | } 40 | 41 | std::function _on_server_message; 42 | // DO NOT IMPLEMENT. This is just to help you understand how the client 43 | // and the server communicate. 44 | }; 45 | 46 | int main() 47 | { 48 | } 49 | -------------------------------------------------------------------------------- /olt_cpp17/solutions/exercise3_solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | struct vec3 7 | { 8 | private: 9 | T _x, _y, _z; 10 | 11 | public: 12 | vec3(T x, T y, T z) 13 | : _x{x}, _y{y}, _z{z} 14 | { 15 | } 16 | 17 | T& x() { return _x; } 18 | const T& x() const { return _x; } 19 | 20 | T& y() { return _y; } 21 | const T& y() const { return _y; } 22 | 23 | T& z() { return _z; } 24 | const T& z() const { return _z; } 25 | }; 26 | 27 | template 28 | const T& get(const vec3& v) 29 | { 30 | if (I == 0) { return v.x(); } 31 | if (I == 1) { return v.y(); } 32 | return v.z(); 33 | } 34 | 35 | template 36 | T& get(vec3& v) 37 | { 38 | if (I == 0) { return v.x(); } 39 | if (I == 1) { return v.y(); } 40 | return v.z(); 41 | } 42 | 43 | namespace std 44 | { 45 | template 46 | struct tuple_size<::vec3> 47 | : std::integral_constant 48 | { 49 | }; 50 | 51 | template 52 | struct tuple_element> 53 | { 54 | using type = T; 55 | }; 56 | } 57 | 58 | int main() 59 | { 60 | const vec3 point{2.f, 1.f, 4.f}; 61 | const vec3 scaling_factors{2.f, 4.f, 10.f}; 62 | 63 | const auto& [px, py, pz] = point; 64 | const auto& [sx, sy, sz] = scaling_factors; 65 | 66 | const vec3 result{px * sx, py * sy, pz * sz}; 67 | 68 | std::printf("%f %f %f\n", 69 | result.x(), result.y(), result.z()); 70 | } 71 | -------------------------------------------------------------------------------- /olt_cpp17/exercise0.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace vittorioromeo 7 | { 8 | namespace draw 9 | { 10 | template 11 | constexpr char* make_circle_pattern(char* output) 12 | { 13 | static_assert(Radius > 0, "Radius must be positive"); 14 | 15 | for(int x = 0; x <= 2 * Radius; x++) 16 | { 17 | for(int y = 0; y <= 2 * Radius; y++) 18 | { 19 | const float dist = std::sqrt((x - Radius) * (x - Radius) + 20 | (y - Radius) * (y - Radius)); 21 | 22 | if(dist > Radius - 0.5 && dist < Radius + 0.5) 23 | { 24 | *output++ = '*'; 25 | } 26 | else 27 | { 28 | *output++ = ' '; 29 | } 30 | } 31 | 32 | *output++ = '\n'; 33 | } 34 | 35 | return output; 36 | } 37 | 38 | template 39 | constexpr std::array get_circle_pattern() 40 | { 41 | std::array result{}; 42 | 43 | char* last = make_circle_pattern(result.data()); 44 | *last++ = '\0'; 45 | 46 | return result; 47 | } 48 | 49 | } // namespace draw 50 | 51 | namespace util 52 | { 53 | int read_int() 54 | { 55 | int buffer; 56 | std::scanf("%d", &buffer); 57 | return buffer; 58 | } 59 | 60 | } // namespace util 61 | 62 | } // namespace vittorioromeo 63 | 64 | int main() 65 | { 66 | std::printf( 67 | "Please enter 0 to draw a small circle, or 1 to draw a big circle.\n"); 68 | 69 | const int input = vittorioromeo::util::read_int(); 70 | if(input == 0) 71 | { 72 | std::printf( 73 | "%s\n", vittorioromeo::draw::get_circle_pattern<1024, 4>().data()); 74 | } 75 | else if(input == 1) 76 | { 77 | std::printf( 78 | "%s\n", vittorioromeo::draw::get_circle_pattern<1024, 9>().data()); 79 | } 80 | else 81 | { 82 | std::printf("Invalid input %d.\n", input); 83 | return 1; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /olt_cpp17/solutions/exercise0_solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // CHANGE: Use nested `namespace` definitions. 7 | namespace vittorioromeo::draw 8 | { 9 | template 10 | constexpr char* make_circle_pattern(char* output) 11 | { 12 | // CHANGE: Remove redundant static assert message. 13 | static_assert(Radius > 0); 14 | 15 | for(int x{0}; x <= 2 * Radius; x++) 16 | { 17 | for(int y{0}; y <= 2 * Radius; y++) 18 | { 19 | // CHANGE: Use uniform initialization, discover double to float 20 | // narrowing conversion issue. 21 | const auto dist{std::sqrt( 22 | (x - Radius) * (x - Radius) + (y - Radius) * (y - Radius))}; 23 | 24 | if(dist > Radius - 0.5 && dist < Radius + 0.5) 25 | { 26 | *output++ = '*'; 27 | } 28 | else 29 | { 30 | *output++ = ' '; 31 | } 32 | } 33 | 34 | *output++ = '\n'; 35 | } 36 | 37 | return output; 38 | } 39 | 40 | template 41 | constexpr std::array get_circle_pattern() 42 | { 43 | std::array result{}; 44 | 45 | char* last{make_circle_pattern(result.data())}; 46 | *last++ = '\0'; 47 | 48 | return result; 49 | } 50 | 51 | } // namespace vittorioromeo::draw 52 | 53 | namespace vittorioromeo::util 54 | { 55 | int read_int() 56 | { 57 | int buffer; 58 | std::scanf("%d", &buffer); 59 | return buffer; 60 | } 61 | 62 | } // namespace vittorioromeo::util 63 | 64 | int main() 65 | { 66 | std::printf( 67 | "Please enter 0 to draw a small circle, or 1 to draw a big circle.\n"); 68 | 69 | // CHANGE: Use initializer in `if` statement. Notice how `input` is usable 70 | // in all the branches. 71 | if(const auto input{vittorioromeo::util::read_int()}; input == 0) 72 | { 73 | std::printf( 74 | "%s\n", vittorioromeo::draw::get_circle_pattern<1024, 4>().data()); 75 | } 76 | else if(input == 1) 77 | { 78 | std::printf( 79 | "%s\n", vittorioromeo::draw::get_circle_pattern<1024, 9>().data()); 80 | } 81 | else 82 | { 83 | std::printf("Invalid input %d.\n", input); 84 | return 1; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /olt_cpp17/exercise1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace vittorioromeo::util 5 | { 6 | int rnd_int(const int min, const int max) 7 | { 8 | static std::random_device rd; 9 | static std::mt19937 mt(rd()); 10 | 11 | return std::uniform_int_distribution{min, max - 1}(mt); 12 | } 13 | } // namespace vittorioromeo::util 14 | 15 | namespace vittorioromeo::database 16 | { 17 | 18 | enum class outcome : int 19 | { 20 | success = 0, 21 | connection_failure = 1, 22 | unexpected_failure = 2 23 | }; 24 | 25 | enum class record_type : int 26 | { 27 | invalid = -1, 28 | binary = 0, 29 | number = 1, 30 | string = 2, 31 | }; 32 | 33 | struct handle 34 | { 35 | bool _valid_state; 36 | 37 | // 0 and 1 are both `binary`. 38 | // 2 is `number` 39 | // 3 is `string`. 40 | // Any other input is `invalid`. 41 | record_type get_record_type(const int key) 42 | { 43 | if(!_valid_state) 44 | { 45 | // This is a tongue in cheek example of how failure to check 46 | // preconditions can result in "undefined behavior". 47 | return static_cast(1 / 0); 48 | } 49 | 50 | switch(key) 51 | { 52 | case 0: 53 | case 1: return record_type::binary; 54 | case 2: return record_type::number; 55 | case 3: return record_type::string; 56 | } 57 | 58 | return record_type::invalid; 59 | } 60 | }; 61 | 62 | outcome get_handle(handle& out) 63 | { 64 | // Simulate real database connection... 65 | if(util::rnd_int(0, 100) < 95) 66 | { 67 | out = handle{true}; 68 | return outcome::success; 69 | } 70 | else 71 | { 72 | if(util::rnd_int(0, 100) < 50) 73 | { 74 | out = handle{false}; 75 | return outcome::connection_failure; 76 | } 77 | else 78 | { 79 | out = handle{false}; 80 | return outcome::unexpected_failure; 81 | } 82 | } 83 | } 84 | 85 | } // namespace vittorioromeo::database 86 | 87 | int main() 88 | { 89 | vittorioromeo::database::handle h; 90 | vittorioromeo::database::get_handle(h); 91 | 92 | const auto record_type = h.get_record_type(2); 93 | std::printf("Record type is %d\n", static_cast(record_type)); 94 | } 95 | -------------------------------------------------------------------------------- /olt_cpp17/solutions/exercise2_solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Imagine a networked application where messages are exchanged between clients 7 | // and a server. Your goal is to model the messaged described below in a type 8 | // safe manner, using algebraic data types (`std::variant`, `std::optional`). 9 | 10 | // * A client can attempt to connect to a server. The server will reply with 11 | // either success or failure. 12 | 13 | // * A client can attempt to create a new key on the server. Handle possible 14 | // failure cases. 15 | 16 | // * A client can attempt to set a value for a key-value pair on the server. 17 | // Handle possible failure cases. 18 | 19 | // * A client can attempt to read a value from a key-value pair on the server. 20 | 21 | // clang-format off 22 | namespace clientmsg 23 | { 24 | struct connect { }; 25 | struct create_key { std::string _key; std::optional _value; }; 26 | struct set_value { std::string _key; std::string _value; }; 27 | struct get_value { std::string _key; }; 28 | } 29 | 30 | namespace servermsg 31 | { 32 | // Responses to client messages which attempt a connection. 33 | struct client_connect_success { }; 34 | struct client_connect_failure { int _error_code; }; 35 | 36 | // Responses to client messages which access key-value pairs. 37 | struct key_already_present { }; 38 | struct key_non_existent { }; 39 | struct key_action_success { }; 40 | struct value { std::string _value; }; 41 | } 42 | 43 | struct client_message 44 | { 45 | std::variant< 46 | clientmsg::connect, 47 | clientmsg::create_key, 48 | clientmsg::set_value, 49 | clientmsg::get_value 50 | > _payload; 51 | }; 52 | 53 | struct server_message 54 | { 55 | std::variant< 56 | servermsg::client_connect_success, 57 | servermsg::client_connect_failure, 58 | servermsg::key_already_present, 59 | servermsg::key_non_existent, 60 | servermsg::key_action_success, 61 | servermsg::value 62 | > _payload; 63 | }; 64 | // clang-format on 65 | 66 | struct client 67 | { 68 | void send(const client_message& message, const char* server_ip) 69 | { 70 | // DO NOT IMPLEMENT. This is just to help you understand how the client 71 | // and the server communicate. 72 | 73 | (void)message; 74 | (void)server_ip; 75 | } 76 | 77 | std::function _on_server_message; 78 | // DO NOT IMPLEMENT. This is just to help you understand how the client 79 | // and the server communicate. 80 | }; 81 | 82 | int main() 83 | { 84 | } 85 | -------------------------------------------------------------------------------- /olt_cpp17/solutions/exercise1_solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace vittorioromeo::util 5 | { 6 | // CHANGE: add `[[nodiscard]]` attribute. 7 | [[nodiscard]] int rnd_int(const int min, const int max) 8 | { 9 | static std::random_device rd; 10 | static std::mt19937 mt(rd()); 11 | 12 | return std::uniform_int_distribution{min, max - 1}(mt); 13 | } 14 | } // namespace vittorioromeo::util 15 | 16 | namespace vittorioromeo::database 17 | { 18 | enum class outcome : int 19 | { 20 | success = 0, 21 | connection_failure = 1, 22 | unexpected_failure = 2 23 | }; 24 | 25 | enum class record_type : int 26 | { 27 | invalid = -1, 28 | binary = 0, 29 | number = 1, 30 | string = 2, 31 | }; 32 | 33 | struct handle 34 | { 35 | bool _valid_state; 36 | 37 | // 0 and 1 are both `binary`. 38 | // 2 is `number` 39 | // 3 is `string`. 40 | // Any other input is `invalid`. 41 | 42 | // CHANGE: add `[[nodiscard]]` attribute. 43 | [[nodiscard]] record_type get_record_type(const int key) 44 | { 45 | if(!_valid_state) 46 | { 47 | // This is a tongue in cheek example of how failure to check 48 | // preconditions can result in "undefined behavior". 49 | return static_cast(1 / 0); 50 | } 51 | 52 | switch(key) 53 | { 54 | // CHANGE: add `[[fallthrough]]` attribute. You should compile 55 | // your code with `-Wfallthrough` to detect possible 56 | // problems. 57 | case 0: [[fallthrough]]; 58 | case 1: return record_type::binary; 59 | case 2: return record_type::number; 60 | case 3: return record_type::string; 61 | } 62 | 63 | return record_type::invalid; 64 | } 65 | }; 66 | 67 | // CHANGE: add `[[nodiscard]]` attribute. This is the important one! As an 68 | // alternative, it can also be applied to the `outcome` type. 69 | [[nodiscard]] outcome get_handle(handle& out) 70 | { 71 | // Simulate real database connection... 72 | if(util::rnd_int(0, 100) < 95) 73 | { 74 | out = handle{true}; 75 | return outcome::success; 76 | } 77 | else 78 | { 79 | if(util::rnd_int(0, 100) < 50) 80 | { 81 | out = handle{false}; 82 | return outcome::connection_failure; 83 | } 84 | else 85 | { 86 | out = handle{false}; 87 | return outcome::unexpected_failure; 88 | } 89 | } 90 | } 91 | 92 | } // namespace vittorioromeo::database 93 | 94 | int main() 95 | { 96 | vittorioromeo::database::handle h; 97 | 98 | // CHANGE: inspect the result of `get_handle`. The compiler will warn you 99 | // after adding `[[nodiscard]]`! 100 | if(const auto rc = vittorioromeo::database::get_handle(h); 101 | rc != vittorioromeo::database::outcome::success) 102 | { 103 | std::printf("Database connection failed\n"); 104 | return 1; 105 | } 106 | 107 | const auto record_type = h.get_record_type(2); 108 | std::printf("Record type is %d\n", static_cast(record_type)); 109 | } 110 | --------------------------------------------------------------------------------