diff --git a/5-UsefulConceptsAndSTL/2-RAII.ipynb b/5-UsefulConceptsAndSTL/2-RAII.ipynb index 2d3c17e3aa2d3a2576c74dab4a8b655969b305ac..6e3a7031c910a54aa4ba9bbf6d8c12e1d72d9ea3 100644 --- a/5-UsefulConceptsAndSTL/2-RAII.ipynb +++ b/5-UsefulConceptsAndSTL/2-RAII.ipynb @@ -31,7 +31,7 @@ "\n", "C++ provides in fact the best of both worlds: a way to provide safe freeing of memory as soon as possible... provided you know how to adequately use it.\n", "\n", - "The **Ressource Acquisition Is Initialization** or **RAII** is the key mechanism for this: the idea is just to use an object with:\n", + "The **Ressource Acquisition Is Initialization** or **RAII** idiom is the key mechanism for this; the idea is just to use an object with:\n", "* The constructor in charge of allocating the ressources (memory, mutexes, etc...)\n", "* The destructor in charge of freeing all that as soon as the object becomes out-of-scope.\n", "\n", @@ -191,5 +191,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/5-UsefulConceptsAndSTL/4-AssociativeContainers.ipynb b/5-UsefulConceptsAndSTL/4-AssociativeContainers.ipynb index 7f7adb79e9b862fbe29b98fa4ae7f4fbb5f38158..427b472c7b38ce52a32cf6fa0f16dea2b4a45cd5 100644 --- a/5-UsefulConceptsAndSTL/4-AssociativeContainers.ipynb +++ b/5-UsefulConceptsAndSTL/4-AssociativeContainers.ipynb @@ -129,7 +129,7 @@ "source": [ "#### C++ 17: Structure binding\n", "\n", - "C++ 17 introduced an alternate new syntax is like a lot that is called **structure bindings**:" + "C++ 17 introduced an alternate new syntax I like a lot that is called **structure bindings**:" ] }, { @@ -146,8 +146,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As you see, the syntax allocates on the fly variable (here references) for the first and second element of the pair.\n", - "\n", + "As you see, the syntax allocates on the fly variable (here references) for the first and second element of the pair, making the code much more expressive.\n", "You may read more on them [here](https://www.fluentcpp.com/2018/06/19/3-simple-c17-features-that-will-make-your-code-simpler/); we will use them again in this notebook." ] }, @@ -221,7 +220,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "See here that David was correctly inserted... but Alice was unchanged! \n", + "See here that Dave was correctly inserted... but Alice was unchanged! \n", "\n", "In fact `insert` returns a pair:\n", "* First is an iterator to the newly inserted element, or to the position of the one that made the insertion fail.\n", @@ -274,6 +273,8 @@ "metadata": {}, "outputs": [], "source": [ + "// In 2022 seems to fail with Xeus-Cling, but it is perfectly code I heartily recommend over the more clunky notation above.\n", + "\n", "const auto& [iterator, was_properly_inserted] = age_list.insert({\"Alice\", 32});\n", "if (!was_properly_inserted)\n", " std::cerr << \"Insertion of Alice failed\" << std::endl;" @@ -283,7 +284,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "That's something I dislike in this very useful class: error handling is not up to my taste as you have to remember to check explicitly all went right...\n", + "That's something I dislike in this very useful class: error handling is not up to my taste as you have to remember to check explicitly all went right... (this is the discussion we had previously about error codes all over again...)\n", "\n", "### Access to one element: don't use `operator[]`!\n", "\n", @@ -319,7 +320,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "So if you provide a wrong key, it doesn't yell and create on the spot a new entry, filling the associated value with the default constructor for the type...\n", + "So if you provide a wrong key, it doesn't yell and create instead on the spot a new entry, filling the associated value with the default constructor for the type...\n", "\n", "To do it properly (but more verbose!), use the `find()` method (if you're intrigued by the use of iterator there, we will present them more in details in the notebook about [algorithms](./7-Algorithms.ipynb)):" ] @@ -387,7 +388,7 @@ "\n", "You may use your own objects as keys, provided that:\n", "\n", - "* Either you define `operator<` for it. `operator==` doesn't matter: even in `find` it is really `operator<` that is used!\n", + "* Either you define `operator<` for it. It is really important to grasp that `operator==` doesn't matter: even in `find` it is really `operator<` that is used!\n", "* Or provide as template parameter the ordering relationship you intend to use.\n", "\n", "**WARNING:** If you're using pointers as keys, make sure to provide an adequate relationship ordering, typically that takes the pointed object relationship. Otherwise from one run to another you might end with different results as the address won't probably be given in the same order..." @@ -410,7 +411,7 @@ "* Access is much more efficient (~O(1), i.e. independant on the number of elements!)\n", "* Memory imprint is bigger.\n", "* Adding new elements is more expensive.\n", - "* The result is not ordered, and there are no rules whatsoever: two runs might not yield the list in the same order.\n", + "* The result is not ordered, and there are no rules whatsoever: two runs on the same computer might not yield the list in the same order.\n", "\n", "The constraint of the key is different too: the key must be **hashable**: there must be a specialization of `std::hash` for the type used for key. It must also define `operator==`.\n", "\n", @@ -432,7 +433,6 @@ "{\n", " std::unordered_map<int, double> list;\n", " list.max_load_factor(0.7f);\n", - " \n", "}" ] }, diff --git a/5-UsefulConceptsAndSTL/5-MoveSemantics.ipynb b/5-UsefulConceptsAndSTL/5-MoveSemantics.ipynb index bbc9d78d0135365573f78fecd3ae8e76ed0aede6..298c4ff7d25046acc89c7753e759dbb4223dc818 100644 --- a/5-UsefulConceptsAndSTL/5-MoveSemantics.ipynb +++ b/5-UsefulConceptsAndSTL/5-MoveSemantics.ipynb @@ -44,7 +44,7 @@ " public :\n", " \n", " // For next section - don't bother yet\n", - " friend void swap(Text& lhs, Text& rhs);\n", + " friend void Swap(Text& lhs, Text& rhs);\n", "\n", " Text(const char* string);\n", " \n", @@ -89,7 +89,7 @@ "source": [ "Text::Text(const char* string)\n", "{\n", - " std::cout << \"Constructor called\" << std::endl;\n", + " std::cout << \"Constructor called with argument '\" << string << \"'\" << std::endl;\n", " size_ = std::strlen(string) + 1;\n", " data_ = new char[size_] ;\n", " std::copy(string, string + size_, data_);\n", @@ -148,7 +148,7 @@ "source": [ "## A traditional answer: to allow the exchange of internal data\n", "\n", - "By allowing two texts to exchange (swap) their internal data, we can rewrite our program in a much more economical way in terms of execution time:" + "By allowing two `Text` objects to exchange (swap) their internal data, we can rewrite our program in a much more economical way in terms of execution time, by leveraging the fact we know the internal structure of the class:" ] }, { @@ -157,7 +157,7 @@ "metadata": {}, "outputs": [], "source": [ - "void swap(Text& lhs, Text& rhs)\n", + "void Swap(Text& lhs, Text& rhs)\n", "{ \n", " unsigned int tmp_size = lhs.size_;\n", " char* tmp_data = lhs.data_;\n", @@ -180,7 +180,7 @@ " Text t2(\"Hello\") ;\n", " \n", " // Swap of values:\n", - " swap(t1, t2);\n", + " Swap(t1, t2);\n", " \n", " std::cout << t1 << \" \" << t2 << std::endl;\n", "}" @@ -243,7 +243,7 @@ "source": [ "#include <iostream>\n", "\n", - "void print(std::string& lvalue)\n", + "void Print(std::string& lvalue)\n", "{\n", " std::cout << \"l-value is \" << lvalue << std::endl;\n", "}" @@ -256,7 +256,7 @@ "outputs": [], "source": [ "{\n", - " print(\"hello\") ; // Compilation error: \"hello\" is a r-value!\n", + " Print(\"hello\") ; // Compilation error: \"hello\" is a r-value!\n", "}" ] }, @@ -276,7 +276,7 @@ "#include <iostream>\n", "#include <string>\n", "\n", - "void print_by_copy(std::string value) // no reference here!\n", + "void PrintByCopy(std::string value) // no reference here!\n", "{\n", " std::cout << \"l- or r- value is \" << value << std::endl;\n", "}" @@ -289,7 +289,7 @@ "outputs": [], "source": [ "{\n", - " print_by_copy(\"hello\") ; // Ok!\n", + " PrintByCopy(\"hello\") ; // Ok!\n", "}" ] }, @@ -306,7 +306,7 @@ "metadata": {}, "outputs": [], "source": [ - "void print_by_const_ref(const std::string& lvalue) \n", + "void PrintByConstRef(const std::string& lvalue) \n", "{\n", " std::cout << \"l-value is \" << lvalue << std::endl;\n", "}" @@ -319,7 +319,7 @@ "outputs": [], "source": [ "{\n", - " print_by_const_ref(\"hello\") ; // Ok!\n", + " PrintByConstRef(\"hello\") ; // Ok!\n", "}" ] }, @@ -395,16 +395,16 @@ "#include <iostream>\n", "#include <vector>\n", "\n", - "void print_double(const std::vector<int>& vec)\n", + "void PrintDouble(const std::vector<int>& vec)\n", "{\n", - " std::cout << \"print_double for l-value\" << std::endl;\n", + " std::cout << \"PrintDouble for l-value\" << std::endl;\n", " std::vector<int> copy(vec);\n", " \n", " for (auto& item : copy)\n", " item *= 2;\n", " \n", " for (auto item : copy)\n", - " std::cout << item << \"\\t\";\n", + " std::cout << item << ' ';\n", " \n", " std::cout << std::endl;\n", "}" @@ -418,7 +418,7 @@ "source": [ "{\n", " std::vector<int> primes { 2, 3, 5, 7, 11, 13, 17, 19 }; \n", - " print_double(primes);\n", + " PrintDouble(primes);\n", "}" ] }, @@ -437,14 +437,14 @@ "source": [ "#include <iostream>\n", "\n", - "void print_double(std::vector<int>&& vec)\n", + "void PrintDouble(std::vector<int>&& vec)\n", "{\n", - " std::cout << \"print_double for r-value\" << std::endl;\n", + " std::cout << \"PrintDouble for r-value\" << std::endl;\n", " for (auto& item : vec)\n", " item *= 2;\n", " \n", " for (auto item : vec)\n", - " std::cout << item << \"\\t\";\n", + " std::cout << item << ' ';\n", " \n", " std::cout << std::endl;\n", "}" @@ -457,7 +457,35 @@ "outputs": [], "source": [ "{\n", - " print_double(std::vector<int>{ 2, 3, 5, 7, 11, 13, 17, 19 });\n", + " PrintDouble(std::vector<int>{ 2, 3, 5, 7, 11, 13, 17, 19 });\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But if used it upon a `std::vector` that can't be modified, we can fall back to the other instantiations which doesn't harm the content of the vector:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "{\n", + " std::vector<int> initial_vector{ 2, 3, 5, 7, 11, 13, 17, 19 };\n", + " PrintDouble(initial_vector);\n", + " \n", + " std::cout << \"And initial vector is properly unchanged: [ \";\n", + " \n", + " for (const auto item : initial_vector)\n", + " std::cout << item << ' ';\n", + " \n", + " std::cout << \"]\" << std::endl;\n", + " \n", + " \n", "}" ] }, @@ -478,7 +506,7 @@ "source": [ "{\n", " std::vector<int> primes { 2, 3, 5, 7, 11, 13, 17, 19 }; \n", - " print_double(static_cast<std::vector<int>&&>(primes));\n", + " PrintDouble(static_cast<std::vector<int>&&>(primes));\n", "}" ] }, @@ -499,8 +527,8 @@ "source": [ "{\n", " std::vector<int> primes { 2, 3, 5, 7, 11, 13, 17, 19 }; \n", - " print_double(std::move(primes)); // strictly equivalent to the static_cast in former cell!\n", - "}\n" + " PrintDouble(std::move(primes)); // strictly equivalent to the static_cast in former cell!\n", + "}" ] }, { @@ -647,7 +675,7 @@ "metadata": {}, "outputs": [], "source": [ - "// Snippet not complete complete enough to work.\n", + "// Snippet not complete enough to work.\n", "\n", "class Matrix; // forward declaration - don't bother yet!\n", "\n", @@ -691,8 +719,6 @@ "{\n", " public :\n", " \n", - " friend void swap(Text2& lhs, Text2& rhs);\n", - "\n", " Text2(const char* string);\n", " \n", " // Copy constructor.\n", @@ -828,7 +854,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "With all this move semantics, the operations above are comparable to what we achieved with the `swap` function for `Text` earlier... with the additional benefit that this semantic is not only used for swapping two values.\n", + "With all this move semantics, the operations above are comparable to what we achieved with the `Swap` function for `Text` earlier... with the additional benefit that this semantic is not only used for swapping two values.\n", "\n", "As already mentioned [there](../3-Operators/4-CanonicalForm.ipynb#[Advanced]-The-true-canonical-class), there are specific rules called __Rule of 0__, __Rule of 3__ and __Rule of 5__, which explains which constructor(s), destructor and assigmnent operator you ought to define for your class.\n", "\n", @@ -847,7 +873,7 @@ "#include <iostream>\n", "#include <string>\n", "\n", - "void do_stuff(std::string&& string)\n", + "void DoStuff(std::string&& string)\n", "{ \n", " std::cout << \"Argument given by r-value is: \" << string << std::endl;\n", " string = \"Bye!\";\n", @@ -862,7 +888,7 @@ "outputs": [], "source": [ "{\n", - " do_stuff(\"Hello!\");\n", + " DoStuff(\"Hello!\");\n", "}" ] }, @@ -928,7 +954,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Unfortunately, C++ 11 committee didn't give a name to this specific call; Scott Meyers first publicized it under the name **universal reference**... and was not followed by the C++ committee that finally chose **forwarding reference**. You may therefore find one or the other term, but the idea behind is exactly the same." + "Unfortunately, C++ 11 committee didn't give immediately a name to this specific call; Scott Meyers first publicized it under the name **universal reference**... and was not followed by the C++ committee that finally chose **forwarding reference**. You may therefore find one or the other term, but the idea behind is exactly the same." ] }, { @@ -999,5 +1025,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/5-UsefulConceptsAndSTL/6-SmartPointers.ipynb b/5-UsefulConceptsAndSTL/6-SmartPointers.ipynb index 511f373cc2a50290d11e21910087feab0865ac32..9ce068b6709ffcc2283a30292e95589b8414e76c 100644 --- a/5-UsefulConceptsAndSTL/6-SmartPointers.ipynb +++ b/5-UsefulConceptsAndSTL/6-SmartPointers.ipynb @@ -45,7 +45,7 @@ "\n", "Smart pointers are clearly a very good way to handle the ownership of a given object. \n", "\n", - "This does not mean they supersede entirely ordinary (often called **raw** or **dumb**) pointers: raw pointers might be a good choice to pass an object as a function parameter (see the discussion for the third question in this [Herb Sutter's post blog](https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/)). The raw pointer behind a smart pointer may be accessed through the `get()` method.\n", + "This does not mean they supersede entirely ordinary (often called **raw** or more infrequently **dumb**) pointers: raw pointers might be a good choice to pass an object as a function parameter (see the discussion for the third question in this [Herb Sutter's post blog](https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/)). The raw pointer behind a smart pointer may be accessed through the `get()` method.\n", "\n", "Both smart pointers exposed below may be constructed directly from a raw pointer; in this case they take the responsability of destroying the pointer:" ] @@ -138,11 +138,10 @@ "\n", "{\n", " auto ptr = std::make_unique<int>(5);\n", - " auto copy = std::move(ptr); \n", - " \n", - "// std::cout << \"Beware as now there are no guarantee upon the content of ptr: \" << *ptr << std::endl;\n", - "// < This line is invalid (using `ptr` after move is undefined behaviour) and makes Xeus-cling crash\n", + " auto copy = std::move(ptr);\n", " \n", + " // std::cout << \"Beware as now there are no guarantee upon the content of ptr: \" << *ptr << std::endl;\n", + " // < This line is invalid (using `ptr` after move is undefined behaviour) and makes Xeus-cling crash\n", "}" ] }, @@ -171,9 +170,8 @@ " public:\n", " \n", " Content(std::string&& text); // notice: no default constructor!\n", - " \n", - " \n", - " const std::string& GetValue() const;\n", + " \n", + " const std::string& GetValue() const;\n", " \n", " private:\n", " \n", @@ -376,7 +374,7 @@ "\n", "`shared_ptr` are clearly useful, but you should always wonder first if you really need them: for most uses a `unique_ptr` eventually seconded by raw pointers extracted by `get()` is enough.\n", "\n", - "There is also a risk of not releasing properly the memory is there is a circular dependancy between two `shared_ptr`. A variation of this pointer named `weak_ptr` enables to circumvent this issue, but is a bit tedious to put into motion. I have written in [appendix](../7-Appendix/WeakPtr.ipynb) to describe how to do so.\n", + "There is also a risk of not releasing properly the memory is there is a circular dependancy between two `shared_ptr`. A variation of this pointer named `weak_ptr` enables to circumvent this issue, but is a bit tedious to put into motion. I have written in [appendix](../7-Appendix/WeakPtr.ipynb) a notebook to describe how to do so.\n", "\n" ] }, @@ -628,5 +626,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/5-UsefulConceptsAndSTL/7-Algorithms.ipynb b/5-UsefulConceptsAndSTL/7-Algorithms.ipynb index fb32ad6d4d23ef72fd8b1c5e54a261f80f098d32..0424809048c7cd49a9fca95a1d0ff88438bbd283 100644 --- a/5-UsefulConceptsAndSTL/7-Algorithms.ipynb +++ b/5-UsefulConceptsAndSTL/7-Algorithms.ipynb @@ -29,7 +29,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Even if C++ can't be qualified as a _batteries included_ language like Python (until C++ 17 there was no proper filesystem management, and the support of this feature was still shaky at best in several STL implementations one year ago...), there are plenty of algorithms that are already provided within the STL.\n", + "Even if C++ can't be qualified as a _batteries included_ language like Python (until C++ 17 there was no proper filesystem management, and the support of this feature was still shaky at best in several STL implementations circa 2019...), there are plenty of algorithms that are already provided within the STL.\n", "\n", "We won't obviously list them all here - the mighty \\cite{Josuttis2012} which is more than 1000 pages long don't do it either! - but show few examples on how to use them. For instance, many STL algorithms rely upon iterators: this way a same algorithm may be used as well on `std::vector`, `std::list`, and so on...\n", "\n", @@ -273,7 +273,7 @@ "source": [ "The issue is that the memory is not allocated first: the algorithm doesn't provide the memory at destination! (the reason is that an algorithm is as generic as possible; here `std::copy_if` is expected to work as well with `std::set`... and `std::vector` and `std::set` don't use the same API to allocate the memory).\n", "\n", - "Of course, in some cases it is tricky to know in advance what you need, and here computing it previously with `std::count_if` add an additional operation. There is actually a way to tell the program to insert the values by `push_back` with `std::back_inserter`; it might be a good idea to reserve enough memory to use this method without recopy:" + "Of course, in some cases it is tricky to know in advance what you need, and here computing it previously with `std::count_if` adds an additional operation. There is actually a way to tell the program to insert the values by `push_back` with `std::back_inserter`; it might be a good idea to reserve enough memory to use this method without recopy:" ] }, { @@ -479,7 +479,7 @@ " \n", " std::cout << \"The even values are: \";\n", " \n", - " for (auto it = int_vec.cbegin(); it != logical_end; ++it)\n", + " for (auto it = int_vec.cbegin(); it != logical_end; ++it) // see the use of `logical_end` here!\n", " std::cout << *it << \" \";\n", " \n", " std::cout << std::endl;\n", @@ -541,7 +541,7 @@ "\n", "It is also important to highlight that while the STL algorithms may provide you efficiency (this library is written by highly skilled engineers after all), this is not its main draw: the algorithms are written to be as generic as possible. The primary reason to use them is to allow you to think at a higher level of abstraction, not to get the fastest possible implementation. So if your ~~intuition~~ benchmarking has shown that the standard library is causing a critical slowdown, you are free to explore classic alternatives such as [loop unrolling](https://en.wikipedia.org/wiki/Loop_unrolling) - that's one of the strength of the language (and the STL itself opens up this possibility directly for some of its construct - you may for instance use your own memory allocator when defining a container). For most purposes however that will not be necessary.\n", "\n", - "FYI, C++ 20 introduces a completely new way to deal with algorithms, which does not rely on direct use of iterators but instead on a range library. This leads to a syntax which is more akin to what is done in other languages - see for instance this example lifted from this [blog post](https://www.modernescpp.com/index.php/c-20-the-ranges-library):\n" + "FYI, C++ 20 introduces a completely new way to deal with algorithms, which does not rely on direct use of iterators but instead on a range library. This leads to a syntax which is more akin to what is done in other languages - see for instance this example [@Coliru](https://coliru.stacked-crooked.com/a/efbfb359b4dfa6ee):" ] }, { @@ -559,12 +559,13 @@ "int main(int argc, char** argv) \n", "{\n", "\n", - " std::vector<int> numbers = {1, 2, 3, 4, 5, 6};\n", + " std::vector<int> numbers = { 3, 5, 12, 17, 21, 27, 28 };\n", " \n", - " auto results = numbers | std::views::filter([](int n){ return n % 2 == 0; })\n", - " | std::views::transform([](int n){ return n * 2; });\n", + " auto results = numbers | std::views::filter([](int n){ return n % 3 == 0; })\n", + " | std::views::transform([](int n){ return n / 3; });\n", " \n", - " for (auto v: results) std::cout << v << \" \"; // 4 8 12\n", + " for (auto v: results) \n", + " std::cout << v << \" \"; // 1 4 7 9\n", "\n", " return EXIT_SUCCESS;\n", "}" @@ -574,7 +575,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Having no first hand experience of it I really can't say more about it but don't be astonished if you meet such a syntax in a C++ program." + "Having no first hand experience of it I really can't say more about it but don't be astonished if you meet such a syntax in a C++ program; you may learn a bit more for instance [here](https://www.modernescpp.com/index.php/c-20-the-ranges-library)." ] }, {