layout: true name: blank styling: styling.css styling-by: Martin Weitzel .stylehint[ Styled with [{{styling}}]({{styling}}) by {{styling-by}} ] --- layout: true name: plain copyright: (CC) BY-SA branding: [Dipl.-Ing. Martin Weitzel](http://tbfe.de) 2020-09-22 … 2020-09-24 customer: [SINAMICS DI MC GMC R&D SWRT 1, 2, 3 + 6](https://new.siemens.com/global/en/products/drives/sinamics.html) {{header}} .pagefooter[ {{copyright}}: {{branding}} {{customer}} .siesim-logo[] ] --- layout: true name: linkinfoPreview copyright: (CC) BY-SA branding: [Dipl.-Ing. Martin Weitzel](http://tbfe.de) 2020-09-22 … 2020-09-24 customer: [SINAMICS DI MC GMC R&D SWRT 1, 2, 3 + 6](https://new.siemens.com/global/en/products/drives/sinamics.html) {{header}} .infographicSmall[ [](InfoGraphics/{{graphic}}.png "Click to open – add [CTRL+] SHIFT for new [tabbed] window") ] .pagefooter[ {{copyright}}: {{branding}} {{customer}} .siesim-logo[] ] --- layout: true name: linkinfoPage copyright: (CC) BY-SA branding: [Dipl.-Ing. Martin Weitzel](http://tbfe.de) 2020-09-22 … 2020-09-24 customer: [SINAMICS DI MC GMC R&D SWRT 1, 2, 3 + 6](https://new.siemens.com/global/en/products/drives/sinamics.html) .infographicFull[ [](InfoGraphics/{{graphic}}.png "Click to open – add [CTRL+] SHIFT for new [tabbed] window") ] --- layout: false template: blank name: frontmatter .title[ SINAMICS ADVANCED C++ ] .subtitle[ (Selected Topics) ] .author.pull-left[ Durchführung Dipl.-Ing. Martin Weitzel Technische Beratung für EDV http://tbfe.de ] .client.pull-right[ Siemens SINAMICS DI MC GMC R&D SWRT 1 ,2 und 6 2020-09-22 to 2020-09-24 https://siemens.com ] --- template: plain name: main_toc header: ## Pre-Planned And Optional Topics * [Modern C++ Features in SINAMICS](#modern_cpp_features) * [C++ Style Casts](#cpp_style_casts) * [Memory Management](#memory_management) * [C++ Templates](#cpp_templates) * [Standard Template Library](#cpp_stl) * [Misc Stuff (Pitfalls, Idioms, and More)](#misc_stuff) .N.center[ **Your Questions And Suggestions Are Always Welcome!**._[] ] .F[: Unless, of course, you use this for self study. But still then feel free to contact the author via mail. ] --- template: plain header: ### General Structure Of Document * Level-2 headers used for major sections (like the one within this is a part of) * Level-3 headers used for thematic section (like exactly this page has) * Level-4 headers used for thematic sub-section * Level-5 headers used for individual topic section * Level-6 headers (optionally used as necessary) * More Details (maybe skipped on cursory review) * Info-Graphics (for things better explained graphically) * Live-Demos (based on the [Godbolt's Compiler Explorer]) * DIY-Explorations (details typically hidden) [Godbolt's Compiler Explorer]: https://godbolt.org --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/5ry6bk | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/j1Tv3n | | | https://godbolt.org/z/q53zY3 | | | https://godbolt.org/z/bqs4cv | | | https://godbolt.org/z/hWMPTc | | | https://godbolt.org/z/6v3GsW | | Final version here: | https://godbolt.org/z/5q65sd | ] * review the example * point out *Source* window * point out *Executor* window * rearranging windows * drag windows by their tabs * hide and show windows * adding a *Compiler* window * show compiler selection and options * demonstrate identifying compile errors * demonstrate comparing assembler code .Y[ Hit P to show/hide suggestions for DIY explorations. ] ??? ###### DIY-Exploration (Basic) * Restart the example from the live-demo. * Drag the *Executor* window to different positions. * Drag the *Source* window to different positions. * Hide any of both windows and show again. * Add a *Compiler* window showing assembler output. * Arrange window so that … * … *Source* window is on the left covering the full height; * … *Compiler* window is on the right at the top half; * … *Executor* window is on the right at the bottom half. ###### DIY-Exploration (Intermediate) * Completely remove the *Executor* windows. * In its place add another *Compiler* window. * Use different compiler options in both *Compiler* windows so that you get different variants of assembler code. * Add a *Diff View* window comparing the assembler output. * Place the window just added add the bottom of the screen and make it cover the full width there. ##### DIY-Exploration (Advanced) * Use that last window arrangement to compare the code generated for C- and C++-style output (i.e. `operator<<` vs. `printf` --- template: plain name: modern_cpp_features header: ## Modern C++ Features in SINAMICS ---------------------------------------------------------------------------------- * [`cstdint`](#cstdint) * [Enumerations](#enum_vs_class_enum) * [Override/Final/NVI](#override_final) * [Unified Initialization](#unified_init) * [Initializer Lists](#initializer_lists) * [Constructors](#ctors) * [Compile Time Evaluation](#compile_time_evaluation) * [`static_assert`](#static_assert) * [Unrestricted `union`s](#unrestricted_unions) * [`nullptr`](#nullptr) * [Attributes](#attributes) ---------------------------------------------------------------------------------- --- template: plain name: cstdint header: ### `cstdint` -------------------------------------------------------------------- * [Fix-Sized Types](#fix_sized_integers) * [Small-Sized Types](#small_sized_integers) * [Fast-Sized Types](#fast_sized_integers) * [`cstdint` Recommendations](#cstdint_recommendations) * [`cstdint` I/O Considerations](#cstdint_io_considerations) -------------------------------------------------------------------- Including the header file `cstdint` makes a number of type aliases in namespace `std` available: .I[ For reference information see: https://en.cppreference.com/w/cpp/header/cstdint and https://en.cppreference.com/w/cpp/header/cinttypes ] --- template: plain name: fix_sized_integers header: #### Fix-Sized Types .N[ These types are only available if they are supported by the hardware. ] .pull-left[ Signed types: * `std::int8_t` * `std::int16_t` * `std::int32_t` * `std::int64_t` ] .pull-right[ Unsigned types: * `std::uint8_t` * `std::uint16_t` * `std::uint32_t` * `std::uint64_t` ] The definitions in `cstdint` are also available for C programs in the header `stdint.h`, putting the type aliases into the global name space. .W[ Only the **unsigned types** guarantee the mapping of integral values to bit positions and a silent overflow behavior (basically doing any arithmetic *modulo 2 raised to the numbers of bits*).._[] ] .F[: Note that C traditionally does not mandate a specific implementation for **signed integral numbers**. Most modern hardware nowadays uses 2-s complement, meaning you get one more number down in the negative range and the wrap-around happens silently between the **highest positive** and the **lowest negative** value. ] --- template: plain name: small_sized_integers header: #### Small-Sized Types .N[ These types will be chosen for smallest memory footprint even if it increases memory access time. ] .pull-left[ Signed types: * `std::int_least8_t` * `std::int_least16_t` * `std::int_least32_t` * `std::int_least64_t` ] .pull-left[ Unsigned types: * `std::uint_least8_t` * `std::uint_least16_t` * `std::uint_least32_t` * `std::uint_least64_t` ] Following is an example how the mapping may look on 64 bit hardware:._[] | type alias | mapped to | type alias | mapped to | |:----------------|:--------------|:-----------------|:---------------------| | `int_least8_t` | `signed char` | `uint_least8_t` | `unsigned char` | | `int_least16_t` | `short` | `uint_least16_t` | `unsigned short` | | `int_least32_t` | `int` | `uint_least32_t` | `unsigned int` | | `int_least64_t` | `long long` | `uint_least64_t` | `unsigned long long` | .F[: Note that in this specific example you can have different overloads of a function for **each different bit length** (assuming you have no overloads for the builtin types to which the aliases map). ] --- template: plain name: fast_sized_integers header: #### Fast-Sized Types .N[ These types will be chosen for fastest memory access even if it increases the more memory footprint. ] .pull-left[ Signed types: * `std::int_fast8_t` * `std::int_fast16_t` * `std::int_fast32_t` * `std::int_fast64_t` ] .pull-left[ Unsigned types: * `std::uint_fast8_t` * `std::uint_fast16_t` * `std::uint_fast32_t` * `std::uint_fast64_t` ] Following is an example how the mapping may look on 64 bit hardware:._[] | type alias | mapped to | type alias | mapped to | |:---------------|:--------------|:----------------|:---------------------| | `int_fast8_t` | `signed char` | `uint_fast8_t` | `unsigned char` | | `int_fast16_t` | `long long` | `uint_fast16_t` | `unsigned long long` | | `int_fast32_t` | `long long` | `uint_fast32_t` | `unsigned long long` | | `int_fast64_t` | `long long` | `uint_fast64_t` | `unsigned long long` | .F[: Note that in this specific example you can have only different overloads of a function for the 8 bit length and **one of the three others** (assuming you have no overloads for the builtin types). ] --- template: plain name: cstdint_recommendations header: #### `cstdint` Recommendations First of all understand that a specific overflow behavior is only guaranteed for `unsigned` types. .W[ For maximum portability make no assumptions how signed types overflow. Instead ensure the result of all calculations stay within the range of representable.._[] ] .F[: This is not an easy task and compiler warnings are not always helpful. At least, before you ignore a warning or even silence it with a cast, be sure to understand what it means, why it was issued, and what the consequences are for the piece of software you are about to write. ] * If the correctness of your code depends on a specific 8-, 16-, 32-, or 64-bit overflow-behavior use the appropriate one `std::uint8_t` to `std::uint_32t`), otherwise … * … if you need a single variable only, the overflow-behavior required is only a minimum limit, and fastest possible execution is be desirable, chose from the appropriate *`least`*`_t` aliases; * … if you need to put storage space for many such variables into an array, chose from use appropriate *`fast`*`_t` aliases. --- template: plain name: cstdint_io_considerations header: #### `cstdint` I/O-Considerations As the integral types from `cstdint` may be mapped differently depending om the compiler, be sure to understand the consequences this has for I/O: .N[ A simple technique that works generically is to convert the integral value to the appropriate largest type (`std::intmax_t` or `std::uintmax_t)`).._[] ] .F[: In C-style output you will still have to use the appropriate format specifier (from `PRI`*`MAX`) but you have no need to adapt it when you change between different sizes or between "exact" vs. "least" vs. "fast" variant. ] Alternatively: * In C-style I/O use the appropriate macros from this table: https://en.cppreference.com/w/cpp/types/integer#Format_macro_constants * In C++-style I/O overload resolution be aware the 8-bit (`char`-) sized types will * **not** be shown as their numeric code point value * but as the printable character (or its non-printable alternative). --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/vrGhG3 | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/4c41Ec | ] * review the initial example * demonstrate completion * explain basic use of type-traits Hit P to show/hide suggestions for DIY explorations. ??? ###### DIY-Exploration * replicate some of the points shown in the demo code * complete the code for more types --- template: plain name: enum_vs_class_enum header: ### Enumerations -------------------------------------------------------------------- * [Classic Enumerations](#classic_enum) * [Modern Enumerations](#modern_enum) -------------------------------------------------------------------- Modern Enumerations add two features not related to each other and can be found under two terms: * Scoped enumerations * Strongly typed enumerations .N[ More features added to enumeration types with C++11 which are available for modern **and** classic enumerations. ] --- template: plain name: classic_enum header: #### Classic Enumerations Classic enumerations * have always been available in C++ since long before C++98 * but contrary to C * only convert **into** integral types, * not **from** integral types.._[] .N.center[ Since C++11 *Classic Enumerations* optionally allow to forward declarations and to [specify the underlying type](#enum_representation). ] I.e. a function to switch from one to the next enumeration value (given values are consecutive) requires a cast:._[] ``` MyEnum next(MyEnum a) { return static_cast
(a+1); }; ``` .F[: For robust code consider to check for the maximum value and handle it appropriately. ] --- template: plain name: modern_enum header: #### Modern Enumerations *Modern Enumerations* * allow to [specify the underlying type](#enum_representation), * [reduce scope](#typesafe_enum) of `enum` label names, * further [restrict automatic type conversions](#typesafe_enum). The latter means a function to switch from one to the next enumeration value (given values are consecutive) requires two casts:._[] ``` MyEnum next(MyEnum a) { using UT = std::underlying_type_t
; auto tmp = static_cast
(a)+1; return static_cast
(tmp); }; ``` .F[: In addition, the implementation shown above is careful to specify the underlying type as the type to do the arithmetic. If no such type is specified it is taken to be `int` and the following implementation will also do: ``` MyEnum next(MyEnum a) { return static_cast
(static_cast
(a)+1); }; ``` ] --- template: plain name: enum_representation header: #### Underlying Representation * Controlling the memory space taken up. * Allow forward declarations. Example: ``` enum Color : std::uint8_t { Red, Blue, Green }; ``` Or: ``` enum Color : std::uint8_t; // forward declaration ``` .N[ It is a compile time error if the enumeration (constant) values do not fit with the underlying representation. ] .F[: See also SINAMICS C++ Hackathon Page 12 ] ??? ###### DIY-Exploration * Replicate some of the steps shown in the live-demo. * Demonstrate potential of name clashes between classic `enum`s. * Demonstrate advantage of forward declarations. --- template: plain name: scoped_enum header: #### Scoped Enumerations Using the `enum` labels defined in *Modern Enumerations* requires to use the `enum` type name as scope qualifier. Example: ``` enum class Color { Red, Blue, Green }; … Color c = Color::Red; … if (c == Color::Blue) … ``` .N[ Note that scoping the defined enumeration constants with the enumeration type is typically helpful to avoid name space pollution for enumerations defined **outside** any class or namespace. ] Enumerations defined inside classes or namespaces are not as critical for accidental name clashes. .F[: See also: SINAMICS C++ Hackathon Workshop Page 14 ] --- template: plain name: typesafe_enum header: #### Type Safe Enumerations Type safety of _Modern Enumerations_ mean: * They DO NOT automatically convert INTO their underlying type * They DO NOT automatically convert FROM their underlying type .N[ If enumerations are used as tiny bit-sets (build from constants) added type safety may be more of an inconvenience as useful, as it needs to be cast away more often than not: ] Overloading operators may be useful in this situation, e.g.: ``` MyEnum operator|(MyEnum &lhs, MyEnum &rhs) { using UT = std::underlying_type_t
; return static_cast
(static_cast
(lhs) | static_cast
(rhs)); } ``` .F[: See also: SINAMICS C++ Hackathon Workshop Page 17 ] --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/PdPsYn | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/7bh9Yr | | Final version here: | https://godbolt.org/z/P697s1 | ] * review the example * demonstrate some differences * Classic vs. Modern * Restricted Type Conversions * Requirement for Scoping * explain basic use of type-traits .Y[ Hit P to show/hide suggestions for DIY explorations. ] ??? ###### DIY-Exploration (Basic) * replicate some of the points shown in the demo code ###### DIY-Exploration (Advanced) * demonstrate the advantage of using forward declarations * Hint: If you do so with the *Compiler Explorer* you need to do it by putting the forward declaration and a function that **only needs this one** first, then follow the full definition, then the functions that need them. --- template: plain name: override_final header: ### Override/Final/NVI -------------------------------------------------------------------- * [Override](#override) * [Final](#final) * [NVI](#nvi) -------------------------------------------------------------------- --- template: plain name: override header: #### Override By using the qualifier `override` the intend is expressed that a member function overrides an inherited version. * At compile time it is checked: * There actually exists a function somewhere in the base class hierarchy with the same signature. * That function is marked `virtual`. .N.center[ Note that an overridden function can still be **pure virtual** and even `private`. ] --- template: plain name: final header: #### Final By using the qualifier `final` certain possibilities of extending a class are disallowed: * Final can be applied in two locations: * To the definition of a class – preventing to use that class as a base class. * To individual member functions – preventing to overwrite theses in derived classes. .N.center[ Any attempt to violate the restriction results in an error at compile time. ] --- template: plain name: nvi header: #### NVI The NVI-idiom – or [Non Virtual Interface] – demands not to override at the level of the public interface. * Make your member functions `public` and non-`virtual` * or `virtual` and non-`public`. .N.center[ Sometimes this is considered rather a recommendation but a hard rule .] [Non Virtual Interface]: https://en.wikipedia.org/wiki/Non-virtual_interface_pattern --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/5vMoon | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/37EM6T | | | https://godbolt.org/z/TTPEjM | | Final version here: | https://godbolt.org/z/j3ebYc | | Related (CRTP): | https://godbolt.org/z/j3ebYc | ] * review the example .Y[ Hit P to show/hide suggestions for DIY explorations. ] ??? ###### DIY-Exploration (Basic) * replicate some of the points shown in the demo code ###### DIY-Exploration (Intermediate) * modify class `LimitCounter` so that it does not directly reset its `value_` data member but calls its own `Reset` member function * add an overridden `ChainCounter::Reset` so that all of the counter chain connected to `next` is reset too ###### DIY-Exploration (Advanced) * Extend the classes `LimitCounter` and `ChainCounter` so that the counters can also count down * replace the member function `Count` with two different functions `CountUp` and `CountDown` * to class `StopWatch` add a member function `TickDown` * to the `main` program add some tests --- template: plain name: unified_init header: #### Unified Initialization Unified Initialization is also called *Brace Initialization* as it allows the use of curly braces in any initialization context. * Plain variables * typically initialized with an equals sign followed by the initial value; * Objects of class type * requiring a constructor arguments in round parentheses (if any) or * nothing at all (not even empty parentheses!) for the constructor taking no arguments (aka. default constructor); * Members of a class in a member initialization list * Objects of a structured type (like classic `struct`s or native arrays) * making the **equals sign** between the comma separated values enclosed in curly braces **optional**. .W[ In case of constructors with a single argument of type *`T`* be aware of the ambiguity if an `initializer_list` constructor (see next page) is provided too. ] --- template: plain name: initializer_lists header: #### Initializer Lists Comma separated values enclosed in curly braces now have their own data type, `std::initializer_list
`. * It can be used as argument type for functions (especially valuable for container constructors). * If possible the templated type *`T`* will be derived from the initializer list elements. * For that to happen all members need to have the same type or * the expected type can be taken from a formal function argument. .N[ Some automatic conversions are supported in the latter case but **if the value can be preserved through type coercion **. ] --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/6j4van | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/8eTPTv | | | https://godbolt.org/z/MvT5We | | | https://godbolt.org/z/cT16G8 | | Final version here: | https://godbolt.org/z/e66js5 | ] --- template: plain name: ctors header: ### Constructors -------------------------------------------------------------------- * [Direct Member Initialization](#direct_member_init) * [Constructor Delegation](#ctor_delegation) * [Constructor Inheritance](#ctor_inheritance) * [Copying vs. Moving](copying_vs_moving) * [Deleted Default Implementations](#deleted_default) * [Restored Default Implementations](#restored_default) * [Emphasizing Desired Outcome](#emphasizing_desired_outcome) -------------------------------------------------------------------- --- template: plain name: direct_member_init header: #### Direct Member Initialization Direct member initialization means an initial value can be optionally provided together with a member variable definition. * For initializing basic types … * … the classic syntax (equals sign followed by the initial value) … * … but also the unified initialization syntax may be used. * Direct initialization is superseded by initialization through a member initializer list. * An empty list following a member of a basic (builtin) type will cause the same initialization as for global or locally `static` variables. .N[ Therefore direct initialization does not come with a performance penalty but helps to get more reproducible failure behavior in case some initialization has been forgotten. ] --- template: plain name: ctor_delegation header: #### Constructor Delegation Constructor delegation means you can have * a single (or just a few) constructors doing the "real work" and * additional "convenience" constructors supplying typical defaults. .N.center[ In any case the constructor finally selected is determined by the signature (argument count and types) but you may use an additional argument allowing compile-time selection via its type. ] --- template: plain name: ctor_inheritance header: #### Constructor Inheritance Constructor inheritance means a derived class can indicated * that the constructors of its base class should apply too * which may (or may not!) make sense if that class * either has NOT added any member variables at all * or all these can (and do!) use direct member initialization. --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/6oWMb5 | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] * review the example * demonstrating direct member initialization with prepared class * demonstrating constructor delegation with prepared class * demonstrating constructor inheritance with prepared class(es) .Y[ Hit P to show/hide suggestions for DIY explorations. ] ??? ###### DIY-Exploration (Basic) * replicate some of the points shown in the live demo * try more combinations --- template: plain name: copying_vs_moving header: ### Copying vs. Moving Since C++11 operations for copying can be implemented differently than operations for moving. * For a class `MyClass` this applies to construction * with the copy c'tor has the signature `MyClass(const MyClass&)` * and the move c'tor has the signature `MyClass(MyClass&&)` * and assignment * with the copy assignment has the signature `MyClass& operator=(const MyClass&)` * and the move assignment has the signature `MyClass& operator=(MyClass&&)` .N[ If none of these are implemented the compiler supplies default depending on the types of the member variables. ] --- template: plain name: applicability_of_moves header: ### Applicability Of Moves Move operations apply to objects which get destroyed immediately after, as function return values: .pull-left[ Copying will be applied: ``` MyClass a{}; MyClass b{a}; // copy c'tor … MyClass c{ … }; c = b; // copy assignment ``` ] .pull-right[ Moving will be applied: ``` MyClass foo() { …; return MyClass{}; } … MyClass a{foo()}; // move c'tor … MyClass c; c = foo(); // move assignment ``` ] Besides that it can be explicitly allowed to "move from" an object if it subsequently isn't used any longer: .pull-left[ ``` MyClass d{ … }; … c = std::move(d); // may be moved ``` ] .pull-right[ ``` const MyClass d{ … }; … d = std::move(d); // still copied ``` ] --- template: plain name: deleted_default header: ### Deleted Default Implementations If the versions supplied by the compiler are not adequate they must be removed. .N[ A main reason for this is a class "owning" heap memory (lifetime coupled to the class itself). ] Up to C++11 this had to be done by declaring **but not defining** the copying operations: ``` class MyClass { … MyClass(const MyClass&); MyClass& operator=(const MyClass&); }; ``` Beginning with C++11 there is an alternative syntax, recycling the keyword `delete` for a new use case: ``` class MyClass { … MyClass(const MyClass&) =delete; MyClass& operator=(const MyClass&) =delete; }; ``` --- template: plain name: restored_default header: ### Restored Default Implementations Since copy and move can have different implementations in C++11 it can make sense to forbid copying but allow moving: ``` class MyClass { … MyClass(const MyClass&) =delete; MyClass& operator=(const MyClass&) =delete; MyClass(MyClass&&) =default; MyClass& operator=(MyClass&&) =default; }; ``` .N.center[ Such a class would be called "move only" as it just supports to be moved but not to be copied. ] --- template: plain name: emphasizing_desired_outcome header: ### Emphasizing Desired Outcome For clarity in may make sense to explicitly default all operations for which the compiler defaults apply or which should not be available. ``` class MyClass { … // the below could simply be omitted and // the outcome would be exactly the same MyClass() =delete; MyClass(const MyClass&) =default; MyClass& operator=(const MyClass&) =default; MyClass(MyClass&&) =default; MyClass& operator=(MyClass&&) =default; ~MyClass() =default; }; ``` --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/fPGTf9 | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] * review the example * explain the existing test code .Y[ Hit P to show/hide suggestions for DIY explorations. ] ??? ###### DIY-Exploration (Basic) * replicate some of the points shown in the live demo * add some more test code --- template: plain name: compile_time_evaluation header: ### `const`, Compile Time Evaluation And `static_assert` ------------------------------------------------------------- * [`const` For Compile Time Write Protection](#classic_const) * [Compile Time Initialized Data](#constexpr_data) * [Compile Time Executable Functions](#constexpr_functions) * [Compile Time Checked Conditions](#constexpr_conditions) * [Compile Time Assertions Conditions](#static_assert) -------------------------------------------------------------- --- template: plain name: classic_const header: #### `const` For Compile Time Write Protection * Runtime write-protection via `const` is an early addition to C++ over C.._[] .F[: With the C89 standard `const` was introduced to the C language. ] * It is enforced by the compiler, adding no overhead at runtime. * Casts can "remove constness", but … * … their effect technically is "UB" (= Undefined Behavior); * … may lead to inconsistent views on stored data Here is what some compilers do: ``` const int x = 1001; … // compiler may replaced use of x with 1001 // ++x; <--- ERROR ++((int*)&x); // OK ... but does it change x? // x may still be replaced with 1001 // ... but *(&x) may represent 14 now ``` --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/4MyKCU | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/Yzfz18 | | Final version here: | | ] * demonstrate the example as explained in the comments contained * show compile errors for attempting to modify something `const`-qualified. * add a global variable `x` and a function `const_cast_demo` to demonstrate the consequences of modifying a compile-time write protected variable * point out the role of the double function of macro `SHOW` as … * … an easy means to generate output * … to avoid overly eager optimizations when output is of no interest when a comparison of assembler code is the goal * include the source file line number in the output of `SHOW` .Y[ Maybe take some time to review the examples saved during the live-demo. *** Hit P to show/hide suggestions for DIY explorations. ] ??? ###### DIY-Exploration (Basic) * Modify the macro `SHOW` to include the line number. Maybe just *Copy&Paste* it from below: ``` #define SHOW(_) (std::cout << '[' << __LINE__ << ']'\ << '\t' << #_ " --> " << (_) << std::endl) ``` ###### DIY-Exploration (Advanced) * Add a function global variable `x` and a function `const_cast_demo` demonstrating the UB of casting away constness. Maybe just *Copy&Paste* it from below: ``` void const_cast_demo() { const int x = 1001; SHOW(x); // ++x; SHOW(x); SHOW(++*((int*)&x)); SHOW(x); SHOW(*(&x)); } ``` * What compile error is to expect when the comment before `++x` is removed? * Remove the `const` qualifier from `x` and verify the program compiles. * Put the `const` qualifier back in place and remove the line `++x`. * Verify the program compiles. * Try to explain the output. ###### DIY-Exploration (Expert) * Remove (or comment out) the local variable `x` in `const_cast_demo`. * Why does the program crash now? * Replace the C-style cast with a C++-style `const_cast`. * Does this change anything in the assembler code? * How does the assembler code change when you remove the `const` qualifier from the global `x`? * Does the code generated for `++x` (when it is non-`const`) differ from the code generated for: * `++*((int*)&x)` * `++*const_cast
(&x)` * `++const_cast
(x)` --- template: plain name: constexpr_data header: #### Compile Time Initialized Data * Adding the keyword `constexpr` to a variable definition guarantees compile-time initialization. * An initial value must be provided. * If this is an expression it must be possible to evaluate it at compile time. * Variables in scope defined as `constexpr` can always be evaluated at compile time. * Wether Variables in scope defined as `const` can evaluated at compile time on depends how they are initialized. --- template: plain name: constexpr_functions header: #### Compile Time Executable Functions * Adding the keyword `constexpr` to a function definition provides a compile time version of this function. * Such functions are restricted wrt. the language features used. * **Also they must not call any non-`constexpr` function.** * Besides there may also be a runtime version of that function. * It gets used if the `constexpr` function cannot be called at compile time. * Member functions of classes including constructors can also be made `constexpr`. --- template: plain header: ###### More Details * **To make 100% sure a function is actually called at compile time assign the return value to a `constexpr` variable.** * In C++11 `constexpr` functions were restricted to contain nothing more but a `return`-statement. * Since C++14 `constexpr` functions are less restricted but still can not use all language features, e.g.: * **NO** input or output * **NO** heap memory * **NO** exceptions --- template: plain name: constexpr_conditions header: #### Compile Time Checked Conditions * Many modern compilers purge branches of conditionals which … * … can never be reached because **at compile time* … * … the condition can be determined to be never true. * Still the C++ language definition requires … * … such code must not only be syntactically true … * … but also all function calls could be resolved. * C++17 introduced `if constexpr` … * … still requiring syntactical correctness in purged branches … * … but not that functions calls could be resolved. --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/ond41M | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/Tbs9xe | | Final version here: | | ] --- template: plain name: static_assert header: #### Compile Time Assertions Conditions * C++11 introduced `static_assert` * It is a compiler intrinsic that is called like a function: * The first argument must be convertible to a `bool`. * It must be possible to evaluate that value at compile time. * **If it evaluates to `false` a compile error is generated.** * The second argument is included in the error message. * C++14 made the second argument optional .X[ Live-Demo given on request ] --- template: plain name: unrestricted_unions header: #### Unrestricted `union`s Unions have been restricted to pure data structures with trivial constructors in C++98 and c++03. Since C++11 classes with non-trivial constructor can be part of unions. But special member functions (normally generated automatically by the compiler) must be defined explicitly. .X[ Live-Demo given on request ] .F[: See also: SINAMICS C++ Hackathon Workshop Page 84 ] --- template: plain name: nullptr header: #### `nullptr` With C++11 `nullptr` has been introduced with the intend to replace the symbol `NULL` for assignment and test of pointers holding no valid address. * For technical reasons `NULL` cannot be defined as a pointer but as the integral value `0`. * This is no issue as long as the compiler can determine the use of `NULL` takes place in a pointer context: * It then gets converted in the adequate pointer type. * But this cannot happen when there are overloaded functions. .X[ Live-Demo given on request ] .F[: See also: SINAMICS C++ Hackathon Workshop Page 86 ] --- template: plain name: attributes header: #### Attributes Attributes._[] are a way to add Meta-Information * not essential to generate correct code * but maybe helpful, e.g. to customize warnings .N.center[ Typically, attributes not understood by a particular compiler will be silently ignored. ] * Standard Attributes: * C++11: `[[noreturn]]`, `[[carries dependency`]] * C++14: `[[deprecated]]` * C++17: `[[fallthrough]]`, `[[maybe unused]]`, `[[nodiscard]]` .I[ For more information see: https://en.cppreference.com/w/cpp/language/attributes ] .F[: See also: SINAMICS C++ Hackathon Workshop Page 91 ] --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/3Ys17b | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] * demonstrating syntax and some uses of attributes * vary compilers and compiler options .Y[ Maybe take some time to review the examples saved during the live-demo. *** ] --- template: plain name: cpp_style_casts header: ### C++ Style Casts With C++98 a new cast syntax was introduced based on keywords:._[] * `static_cast<` *`type`* `>(` *`expr`* `)` * `const_cast<` *`type`* `>(` *`expr`* `)` * `reinterpret_cast<` *`type`* `>(` *`expr`* `)` .F[: There is a forth one called `dynamic_cast` which is not used as it depends on Runtime Type Identification which is not used in SINAMICS projects. ] --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/89vzM9 | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] * demonstrating some `static_cast` examples * conversions automatically done anyway (but for which the cast may suppress warnings) * reverting conversions automatically done only in one direction * code-generating vs non-code generating applications * (optionally demonstrating some `const_cast` examples) * (optionally demonstrating some `reinterpret_cast` examples) .Y[ Maybe take some time to review the examples saved during the live-demo. *** ] --- template: plain name: memory_management header: ### Memory Management ------------------------------------------------------------------------ * [Heap Memory in Classic C++](#classic_new_and_delete) * [Replacing Native Pointers](#replacing native pointers) * [Smart Pointers vs. Garbage Collection](#garbage_collection) ------------------------------------------------------------------------ --- template: plain name: classic_new_and_delete header: #### Heap Memory in Classic C++ * Heap memory in C++ is allocated with `new` and released with `delete`. * There are two usage forms, one for single objects and one for an array of objects. .W[ Care must be taken to pair the usage correctly as anything else is UB (= Undefined Behavior) which may cause a problem immediately **or at any later point in time.** ] * The usual care must be taken … * … **not to** "forget" to release heap memory no longer in use; * … **not to** release heap memory still in use. .W[ Again, the latter is UB and may be the cause for problems turning up even long after the event. ] --- template: plain name: replacing native pointers header: #### Replacing Native Pointers * The use of native pointers can be minimized by replacing them with *Smart Pointers*: .N.center[ For native pointers **owning** some dynamically allocated resource `std::unique_ptr` is the proper replacement. ] * Turning a resource owning native pointer as data member of some class into an `std::unique_ptr` usually leads to substantial simplifications. .N.center[ For native pointers **sharing** some dynamically allocated resource `std::shared_ptr` is the proper replacement. ] * Turning native pointers holding a shared resource into `std::shared_ptr` … * … is a **sure way** to avoid the premature release of heap memory still in use; * … will **not solve** the problem of potential memory leaks caused by cyclic pointer chains. --- template: linkinfoPage graphic: SmartPointers --- template: plain name: garbage_collection header: #### Smart Pointers vs. Garbage Collection * Many modern programming languages don't require explicitly managing heap memory: * Objects may be created anytime on the heap … * … and a Garbage Collector (aka. GC in short) will care for reclaiming (heap) memory of objects no longer in use. * C++ does not support GC but favors explicit memory management. * **Using `std::shared_ptr` instead of native pointers relieves of the problem of "releasing heap memory too early".** .W[ **Cyclic pointer chains may still cause memory leaks** though the potential for this only exists if an `std::shared_ptr` is member of an object which may directly or indirectly point back to objects of its own class. ] * As a solution to surely break cycles `std::weak_ptr` is provided as a "non-owning" version of `std::shared_ptr`. --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] * showing how heap memory may be managed by explicit `new` and `delete`, including some typical problems * demonstrating the simplifications when a native pointer owning heap memory is replaced with `std::unique_ptr` * demonstrating how memory leaks may be caused by cyclic references and how `std::weak_ptr` may used to avoid them .Y[ Maybe take some time to review the examples saved during the live-demo. *** ] ??? ###### DIY-Exploration (Basic) * Restart the example from the live-demo. * Take some time to go through the similar steps as the Live-Demo did. --- template: plain name: type_alias_vs_class header: ### Type Aliases vs. Classes * There are three ways to introduce new identifiers that can be used as type: * Defining another class. * Classic C `typedef` syntax * Type aliases._[] (covered next) based on the `using` syntax as introduced with C++11. .F[: There are other new options and ways to use this C++11 extension in combination with templates, not (yet) covered here. ] .N[ Only new classes are types on their and is such incompatible with other types, typically causing compile errors when used in the wrong way. ] --- template: plain name: cpp_templates header: ### C++ Templates * [Type Generic Classes and Functions](#class_and_function_templates) * [Template Example: RingBuffer](#ringbuffer_example) * [Avoiding "Code Bloat" via Templates](#template_code_bloat) * [Templates Doing Type Calculations](#type_calculations) * [Standard Type Traits](#std_typetraits) * [Template Example: Extension Points](#extension_points) * [Perfect Forwarding (Using Templates)](#perfect_forwarding) --- template: linkinfoPage name: class_and_function_templates graphic: TemplateBasics --- template: linkinfoPage name: ringbuffer_example graphic: ParametrizedTypesAndSizes --- template: linkinfoPage name: template_code_bloat graphic: ReducingCodeBloat --- template: plain name: type_calculations header: #### Templates Doing Type Calculations .N.center[ Templates can also be used at compile time to do type calculations and transformations.._[] ] * Selection of alternatives builds on template specialization. * Repetition builds on recursion (and needs to use selection too for stopping recursion with a specialization). ``` template
struct add_ptr { using type = T*; }; template
struct remove_ptr; template
struct remove_ptr
{ using type = T; }; template
struct remove_all_ptr
{ using type = T; }; template
struct remove_all_ptr
{ using type = typename remove_all_ptr
::type; }; ``` .F[: Of course, these could also be implemented using classic `typedef`-s. ``` template
struct add_ptr { typedef T* type; }; … template
struct remove_all_ptr
{ typedef T type; }; template
struct remove_all_ptr
{ typedef typename remove_all_ptr
::type type; }; ``` ] --- template: plain name: std_typetraits header: #### Standard Type Traits * C++11 introduce the header file `type_traits`. * It provides a large number of "template compile time functions". As an example, here is how `std::conditional` might be used to optimize handing over of arguments to `RingBuffer::put`: ``` template
class RingBuffer { … // use call by value for arithmetic types of at most // 32 bit size, otherwise use const reference: using arg_type = std::conditional_t< std::is_arithmetic
::value && (sizeof(T) <= 4), T, const T&>; … void put(arg_type e); … }; ``` --- template: linkinfo name: extension_points header: #### Template Example: Extension Points graphic: Example-OpenClosePrinciple --- template: plain name: type_calculations header: #### Perfect Forwarding (Using Templates) * The goal is this .N.center[ Select move vs. copy even **inside** a called function. ] * It works by special rules for template reference arguments. Most easily use "Cookbook style":._[] ``` template
void foo( … , T&& arg, … ) … … // maybe non-move/non-forward uses of `arg` … bar( … , std::forward
(arg), … ); … // Important: NO further use of `arg` below! } ``` .F[: [*"You are not expected to understand this"*][uxc] (or at least not required to), but in case you really, **really**, REALLY want to read the full reference documentation for that feature, you find it here: http://en.cppreference.com/w/cpp/language/template_argument_deduction Though you might prefer a less formal "step by step" introduction: http://thbecker.net/articles/rvalue_references/section_07.html Or how about an excerpt from a talk given by Scott Meyers? https://youtu.be/BezbcQIuCsY?t=248 (for a funny intro [start a bit earlier](https://youtu.be/BezbcQIuCsY?t=165)) ] [uxc]: https://www.netogram.com/hackersdictionaryy.htm#You%20are%20not%20expected%20to%20understand%20this --- template: plain name: cpp_stl header: ### The Standard Template Library * [Standard Data Container](#std_container) * [Iterator Concept](#concept_of_iterators) * [Applying Algorithms](#std_algorithms) --- template: plain name: std_container header: #### Standard Data Container * The STL offers two major categories of data containers: * [Sequence Containers](#stl_sequence_container) and * [Associative Containers](#st_associative_containers) .N[ Both types of containers are **NOT** specified by their data structure but by performance bounds. ] --- template: linkinfo graphic: STL-SequenceContainers --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | https://godbolt.org/z/r4oMTd | | Final version here: | | ] --- template: linkinfo graphic: STL-AssociativeContainers --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] --- template: linkinfo graphic: AlgorithmicComplexity --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] --- template: plain name: concept_of_iterators header: #### Iterator Concept * Iterators provide the "glue" to connect STL-containers to STL-algorithms. .N.center[ But wait, there's more. ] -- * And also standard-conforming other containers to STL-algorithms .N.center[ But wait, there's more. ] -- * Or STL- containers to other algorithms written in terms of iterators. .N.center[ But wait, there's more. ] -- * And of course the forth thinkable combination works too. --- template: linkinfoPage graphic: STL-IteratorUsages --- template: plain name: std_algorithms header: #### Applying Algorithms * Algorithms are applied by specifying (at least) … * … a start position in a container (using an iterator) and … * … an end position in a container (again using an iterator).._[] * More arguments when calling an algorithm can be … * … a destination where the result is to be stored – typically for but not limited to algorithms ending in `_copy`; * … a condition test which elements of the container are affected – typically for but not limited t algorithms ending in `_if`; * … algorithmic specific information as required. * Algorithms often return iterators (or sometimes pairs of iterators) with a meaning depending on the algorithm: * a failed search typically returns the end point; * filling a container typically returns up to which it was filled. .F[: In C++20 algorithms may also be called with a standard conforming container object to be applied from the container begin to end. ] --- template: plain header: ###### Live-Demo .X[ | Initially load this: | https://godbolt.org/z/9zc64s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] --- template: plain name: misc_stuff header: ### Misc Stuff (Pitfalls, Idioms, and More) .N.center[ This part is totally your choice! ] The following are just some suggestions: * [C/C++ Singletons](#c_cpp_singleton) * [NVI = Non Virtual Interface](#non_virtual_interface) * [CRTP = Curiously Recurring Template Pattern](#curiously_recurring_template) * [PIMPLE = Pointer to Implementation](#pointer_to_implementation) * [RAII = Resource Acquisition Is Implementation](#stroustrup_raii) --- template: plain name: c_cpp_singleton) header: #### C/C++ Singletons ###### Live-Demo (given on request) .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] --- template: plain name: non_virtual_interface) header: #### NVI = Non Virtual Interface ###### Live-Demo (given on request) .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] --- template: plain name: curiously_recurring_template) header: #### CRTP = Curiously Recurring Template Pattern ###### Live-Demo (given on request) .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] --- template: plain name: pointer_to_implementation) header: #### PIMPLE = Pointer to Implementation ###### Live-Demo (given on request) .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ] --- template: plain name: stroustrup_raii) header: #### RAII = Resource Acquisition Is Implementation ###### Live-Demo (given on request) .X[ | Initially load this: | https://godbolt.org/z/M5cf3s | | :------------------- | :--------------------------- | | Intermediate steps: | | | Final version here: | | ]