diff --git a/7-Appendix/Crtp.ipynb b/7-Appendix/Crtp.ipynb index 907b52827d60c57d84665ffdde28cbd0b14d4948..c04df80a1b81310b657a7c8df2422377e2f2e41a 100644 --- a/7-Appendix/Crtp.ipynb +++ b/7-Appendix/Crtp.ipynb @@ -27,9 +27,9 @@ "\n", "The syntax is:\n", "\n", - "````\n", + "```c++\n", "class Derived : public Base<Derived>\n", - "````\n", + "```\n", "\n", "i.e. the class derives from a template specialization of a base class... which template argument is the class itself!\n", "\n", @@ -641,9 +641,7 @@ "metadata": {}, "source": [ "\n", - "[© Copyright](../COPYRIGHT.md) \n", - "", - "" + "[© Copyright](../COPYRIGHT.md) \n" ] } ], @@ -693,5 +691,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/7-Appendix/HomemadeException.ipynb b/7-Appendix/HomemadeException.ipynb index 4c88e7602d31ce5a2f07611b8822a2e0f6867887..ca97694a3b7944a668474f37caa4129e7f9291d0 100644 --- a/7-Appendix/HomemadeException.ipynb +++ b/7-Appendix/HomemadeException.ipynb @@ -93,19 +93,28 @@ "\n", "### Indicating the file and line\n", "\n", - "If your code is huge, knowing the exception itself is not enough, if this exception may be thrown from several locations in your code. To identify where the exception was thrown, I use the `__FILE__` and `__LINE__` macro which gives the file and line where they were found in the code (better alternative `std::source_location` is present in C++ 20 but is unfortunately [not well supported yet](https://en.cppreference.com/w/cpp/compiler_support)).\n", + "If your code is huge, knowing the exception itself is not enough, if this exception may be thrown from several locations in your code. To identify where the exception was thrown, I used for a long time the `__FILE__` and `__LINE__` macro which gives the file and line where they were found in the code.\n", "\n", - "So my constructor looks like:\n", + "So my constructor looked like:\n", "\n", - "````\n", + "```c++\n", "HomemadeException(const std::string& msg, const char* invoking_file, int invoking_line);\n", - "````\n", + "```\n", "\n", "It might be a bit wordy but:\n", "* It's safe.\n", "* Print is correct.\n", "* It is easy to figure out where it happened.\n", "\n", + "A better alternative named `std::source_location` was introduced in C++ 20, but it took some time (circa 2023) to be supported properly by both libc++ and libstdc++ (and even currently using libc++ some features don't exactly work [as printed on the can](https://en.cppreference.com/w/cpp/utility/source_location)...)\n", + "\n", + "So current constructor is now:\n", + "\n", + "```c++\n", + "HomemadeException(const std::string& msg, const std::source_location location = std::source_location::current());\n", + "```\n", + "\n", + "\n", "## Implementation of my home-made class\n", "\n", "Here is a transcript of my own exception class, which may be found in [my main project](https://gitlab.inria.fr/MoReFEM/CoreLibrary/MoReFEM/tree/master/Sources/Utilities/Exceptions). Namespace has ben removed (you should add one if you intend to use it in practice):\n", @@ -119,82 +128,90 @@ "metadata": {}, "outputs": [], "source": [ - "#include <string>\n", - "#include <exception>\n", - "\n", - "/*!\n", - " * \\brief Generic class for MoReFEM exceptions.\n", - " */\n", - "class Exception: public std::exception\n", - "{\n", - "public:\n", - "\n", - " /// \\name Special members.\n", - " ///@{\n", + "// Doesn't work on Xeus-cling: C++ 20 on board!\n", "\n", - " /*!\n", - " * \\brief Constructor with simple message.\n", - " *\n", - " * \\param[in] msg Message.\n", - " * \\param[in] invoking_file File that invoked the function or class; usually __FILE__.\n", - " * \\param[in] invoking_line File that invoked the function or class; usually __LINE__.\n", - " */\n", - "\n", - " //@}\n", - " explicit Exception(const std::string& msg, const char* invoking_file, int invoking_line);\n", - "\n", - " //! Destructor\n", - " virtual ~Exception() noexcept override;\n", - "\n", - " //! Copy constructor.\n", - " Exception(const Exception&) = default;\n", - "\n", - " //! Move constructor.\n", - " Exception(Exception&&) = default;\n", + "#include <exception> \n", + "#include <source_location>\n", + "#include <string> \n", "\n", - " //! Copy assignment.\n", - " Exception& operator=(const Exception&) = default;\n", - "\n", - " //! Move assignment.\n", - " Exception& operator=(Exception&&) = default;\n", - "\n", - " ///@}\n", - "\n", - " //! Display the what message from std::exception.\n", - " virtual const char* what() const noexcept override final;\n", "\n", " /*!\n", - " * \\brief Display the raw message (Without file and line).\n", - " *\n", - " * Might be useful if exception is caught to rewrite a more refined message.\n", - " *\n", - " * Before introducing this, we could end up with something like:\n", - " * \\verbatim\n", - " * Exception caught: Exception found at Sources/Model/Internal/InitializeHelper.hxx, line 114: Ill-defined\n", - " * finite element space 1: Exception found at Sources/Model/Internal/InitializeHelper.hxx, line 101:\n", - " * Domain 1 is not defined!\n", - " * \\endverbatim\n", - " *\n", - " * Clearly it is nicer to provide:\n", - " * \\verbatim\n", - " * Exception caught: Exception found at Sources/Model/Internal/InitializeHelper.hxx, line 114: Ill-defined\n", - " * finite element space 1: Domain 1 is not defined!\n", - " * \\endverbatim\n", + " * \\brief Generic class for exceptions used in MoReFEM library \n", " *\n", - " * \\return Exception error message without information about file and line in which the exception was invoked.\n", + " * (https://gitlab.inria.fr/MoReFEM/CoreLibrary/MoReFEM)\n", " */\n", - " const std::string& GetRawMessage() const noexcept;\n", - "\n", - "\n", - "private:\n", - "\n", - " //! The complete what() message (with the location part)\n", - " std::string what_message_;\n", - "\n", - " //! Incomplete message (might be useful if we catch an exception to tailor a more specific message).\n", - " std::string raw_message_;\n", - "\n", - "};\n" + " class Exception : public std::exception\n", + " {\n", + " public:\n", + " /// \\name Special members.\n", + " ///@{\n", + "\n", + " /*!\n", + " * \\brief Constructor with simple message.\n", + " *\n", + " * \\param[in] msg Message.\n", + " * \\param[in] location STL object with relevant information about the calling site. \n", + " */\n", + "\n", + " //@}\n", + " explicit Exception(const std::string& msg,\n", + " const std::source_location location = std::source_location::current());\n", + "\n", + " //! Destructor\n", + " virtual ~Exception() noexcept override;\n", + "\n", + " //! Enable default copy constructor.\n", + " Exception(const Exception& rhs) = default;\n", + "\n", + " //! Enable default move constructor.\n", + " Exception(Exception&& rhs) = default;\n", + "\n", + " //! Enable default assignment operator.\n", + " Exception& operator=(const Exception& rhs) = default;\n", + "\n", + " //! Enable default move assignment operator.\n", + " Exception& operator=(Exception&& rhs) = default;\n", + "\n", + " ///@}\n", + "\n", + " /*!\n", + " * \\brief Display the what message from std::exception.\n", + " *\n", + " * \\return The what() message as a char* (which reads the internal std::string so no risk of deallocation\n", + " * issue).\n", + " */\n", + " virtual const char* what() const noexcept override final;\n", + "\n", + " /*!\n", + " * \\brief Display the raw message (Without file and line).\n", + " *\n", + " * Might be useful if exception is caught to rewrite a more refined message.\n", + " *\n", + " * Before introducing this, we could end up in some cases with something like:\n", + " * \\verbatim\n", + " * Exception caught: Exception found at Sources/Model/Internal/InitializeHelper.hxx, line 114: Ill-defined\n", + " * finite element space 1: Exception found at Sources/Model/Internal/InitializeHelper.hxx, line 101:\n", + " * Domain 1 is not defined!\n", + " * \\endverbatim\n", + " *\n", + " * Clearly it is nicer to provide:\n", + " * \\verbatim\n", + " * Exception caught: Exception found at Sources/Model/Internal/InitializeHelper.hxx, line 114: Ill-defined\n", + " * finite element space 1: Domain 1 is not defined!\n", + " * \\endverbatim\n", + " *\n", + " * \\return Exception error message without information about file and line in which the exception was invoked.\n", + " */\n", + " const std::string& GetRawMessage() const noexcept;\n", + "\n", + "\n", + " private:\n", + " //! The complete what() message (with the location part)\n", + " std::string what_message_;\n", + "\n", + " //! Incomplete message (might be useful if we catch an exception to tailor a more specific message).\n", + " std::string raw_message_;\n", + " };" ] }, { @@ -210,36 +227,42 @@ "metadata": {}, "outputs": [], "source": [ + "// Doesn't work on Xeus-cling: C++ 20 on board!\n", + "\n", + "#include <cstring>\n", "#include <iostream>\n", "#include <sstream>\n", + "#include <type_traits>\n", "#include <utility>\n", "\n", - "// #include \"Exception.hpp\"\n", + "#include \"Exception.hpp\"\n", "\n", "\n", "namespace // anonymous\n", "{\n", - " \n", - " \n", + "\n", + "\n", " //! Call this function to set the message displayed by the exception.\n", - " void SetWhatMessage(const std::string& msg, std::string& what_message, const char* invoking_file, int invoking_line)\n", + " void SetWhatMessage(const std::string& msg, std::string& what_message, const std::source_location location)\n", " {\n", " std::ostringstream stream;\n", - " stream << \"Exception found at \";\n", - " stream << invoking_file << \", line \" << invoking_line << \": \";\n", - " stream << msg;\n", + " stream << \"Exception found in file \" << location.file_name() << \" line \" << location.line();\n", + "\n", + " if (strcmp(location.function_name(), \"\") == 0) // currently Apple clang returns empty!\n", + " stream << \" in function \" << location.function_name();\n", + "\n", + " stream << \": \" << msg;\n", " what_message = stream.str();\n", " }\n", - " \n", - " \n", - "} // namespace anonymous\n", "\n", "\n", - "Exception::Exception(const std::string& msg, const char* invoking_file, int invoking_line)\n", - ": std::exception(),\n", - "raw_message_(msg)\n", + "} // namespace\n", + "\n", + "\n", + "Exception::Exception(const std::string& msg, const std::source_location location)\n", + ": std::exception(), raw_message_(msg)\n", "{\n", - " SetWhatMessage(msg, what_message_, invoking_file, invoking_line);\n", + " SetWhatMessage(msg, what_message_, location);\n", "}\n", "\n", "\n", @@ -272,7 +295,7 @@ "outputs": [], "source": [ "{\n", - " throw Exception(\"Example of exception with home-made class\", __FILE__, __LINE__);\n", + " throw Exception(\"Example of exception with home-made class\", std::source_location::current());\n", "}" ] }, @@ -287,10 +310,23 @@ " std::ostringstream oconv;\n", " oconv << i << \" should have been even!\";\n", " \n", - " throw Exception(oconv.str(), __FILE__, __LINE__);\n", + " throw Exception(oconv.str(), std::source_location::current());\n", "}" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you read [the documentation](https://en.cppreference.com/w/cpp/utility/source_location), we should be able to completely avoid the explicit `std::source_location::current()` call at calling site, according to [CppReference](https://en.cppreference.com/w/cpp/utility/source_location/current):\n", + "\n", + "> If current() is used in a default argument, the return value corresponds to the location of the call to current() at the call site.\n", + "\n", + "That is unfortunately not the behaviour I have observed with libc++ on macOS: the file and line returned is the one from the header file, hence the explicit argument at calling site. Likewise, `function_name()` is empty, hence the test in my `SetWhat()` message.\n", + "\n", + "Nonetheless, we may hope these glitches will be solved at some point, and even with them `std::source_location` is more comfy to use than `__FILE__` and `__LINE__` C macros." + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/7-Appendix/StringView.ipynb b/7-Appendix/StringView.ipynb index 7fc70b58c8d28fd13f1d7d954c82d1775965ad02..9d298bf3f77006506ebfd61bcfb0602d9067d96c 100644 --- a/7-Appendix/StringView.ipynb +++ b/7-Appendix/StringView.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "451d5c67-1c82-4f19-9fd4-b7dd49bac7fb", + "id": "0", "metadata": {}, "source": [ "# [Getting started in C++](./) - [Stringview](./StringView.ipynb)" @@ -10,7 +10,7 @@ }, { "cell_type": "markdown", - "id": "bd83674b-5f88-4d28-b959-ba315588fa67", + "id": "1", "metadata": {}, "source": [ "C++ 17 introduced `std::string_view`, which is basically a sort of viewer over a string (not especially a `std::string`: it works basically as long as it's a chain of contiguous characters, where what is a *character* is defined as first template argument).\n", @@ -21,7 +21,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d6cc7d26-80ba-4c0a-a276-56f9d850bbbe", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -44,7 +44,7 @@ }, { "cell_type": "markdown", - "id": "e565e634-7abb-403a-af47-b46e7e9936e5", + "id": "3", "metadata": {}, "source": [ "Prior to C++ 17, the usual way was to use `const std::string&` as parameter (passing a `std::string` directly would incur copies):" @@ -53,7 +53,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f1562c99-1b25-4314-9903-1932571fe86d", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -74,7 +74,7 @@ }, { "cell_type": "markdown", - "id": "4df1c364-df85-4a4d-a0d3-43218189354d", + "id": "5", "metadata": {}, "source": [ "So what did we gain exactly in the bargain?\n", @@ -84,7 +84,7 @@ }, { "cell_type": "markdown", - "id": "414b36fb-0179-4a0f-a963-2c931c93e12a", + "id": "6", "metadata": {}, "source": [ "Let's write the same *without* the `const` to convince ourselves:" @@ -93,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b7471040-bffb-4a48-8ea0-2ad1fddbf442", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -110,7 +110,7 @@ }, { "cell_type": "markdown", - "id": "f9dc26d5-1b95-4c75-8ca8-901e6962215e", + "id": "8", "metadata": {}, "source": [ "This time it doesn't work at all...\n", @@ -128,13 +128,10 @@ { "cell_type": "code", "execution_count": null, - "id": "157f9843-02d4-4c7b-b062-f4403f911cd8", + "id": "9", "metadata": {}, "outputs": [], "source": [ - "// Beware: the code in this cell seems to disturb Xeus-cling ability to write to std::cout...\n", - "// A ticket has been emitted: https://github.com/jupyter-xeus/xeus-cling/issues/405\n", - "\n", "#include <iostream>\n", "#include <string>\n", "#include <string_view>\n", @@ -147,7 +144,7 @@ }, { "cell_type": "markdown", - "id": "d8be6dd3-6ecf-4861-a556-35fe159e3445", + "id": "10", "metadata": {}, "source": [ "C++ 20 will expand on this possibility for other contiguous containers with [`std::span`](https://en.cppreference.com/w/cpp/container/span)." @@ -155,7 +152,7 @@ }, { "cell_type": "markdown", - "id": "d46d567d-50e9-4fef-a689-deaaff8d059a", + "id": "11", "metadata": {}, "source": [ "[© Copyright](../COPYRIGHT.md)" diff --git a/7-Appendix/Switch.ipynb b/7-Appendix/Switch.ipynb index 24556ecb8adb2f2bb7207b72e294ab9872935ffb..49a18864a084ef9d999c37bd684edf3627b7cae3 100644 --- a/7-Appendix/Switch.ipynb +++ b/7-Appendix/Switch.ipynb @@ -227,6 +227,8 @@ "metadata": {}, "outputs": [], "source": [ + "// Fails under Xeus-cling in 2024 but it is srtrictly valid code!\n", + "\n", "#include <iostream>\n", "\n", "{\n", @@ -245,14 +247,19 @@ "}" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[@Coliru link](https://coliru.stacked-crooked.com/a/b860bc760d9c099a) for the snippet above." + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", - "[© Copyright](../COPYRIGHT.md) \n", - "", - "" + "[© Copyright](../COPYRIGHT.md) \n" ] } ], diff --git a/7-Appendix/WeakPtr.ipynb b/7-Appendix/WeakPtr.ipynb index 140a35fad8a858f3494abca67bedbf30873adfaa..01eeb1d878b81f989f3f4369bb4f8cec2cd8c5cc 100644 --- a/7-Appendix/WeakPtr.ipynb +++ b/7-Appendix/WeakPtr.ipynb @@ -225,7 +225,7 @@ "\n", "It is only used for storage; you can't use the object underneath directly and have to build a `shared_ptr` from it before use with the method `lock()`. \n", "\n", - "There are just two modifications to do (used to be done here but circa 2021 Xeus emit an error so the code will be put instead [@Coliru](http://coliru.stacked-crooked.com/a/2013674aee9983d5)):" + "There are just two modifications to do (used to be done here but since 2021 Xeus emit an error (always there in 2024...) so the code will be put instead [@Coliru](http://coliru.stacked-crooked.com/a/6e32de57ca65b6fb)):" ] }, { @@ -241,7 +241,7 @@ "metadata": {}, "outputs": [], "source": [ - "std::vector<std::weak_ptr<Child2>> children_;" + "std::vector<std::weak_ptr<Child>> children_;" ] }, { @@ -278,7 +278,7 @@ "\n", "### Proper usage of `lock()`\n", "\n", - "However our code is currently not robust: we forget to check whether the underlying objects are still valid. Let's consider the following main ([@Coliru](http://coliru.stacked-crooked.com/a/106bf662aba30b43) - don't be surprised segmentation fault is fully expected!):" + "However our code is currently not robust: we forget to check whether the underlying objects are still valid. Let's consider the following main ([@Coliru](http://coliru.stacked-crooked.com/a/aa96edd87fbd1f9b) - don't be surprised segmentation fault is fully expected!):" ] }, { @@ -287,17 +287,19 @@ "metadata": {}, "outputs": [], "source": [ + "// Doesn't work in Xeus-cling!\n", + "\n", "int main(int argc, char** argv)\n", "{\n", - " auto father = std::make_shared<Father2>(\"Darth Vader\"); \n", - " auto boy = std::make_shared<Child2>(\"Luke\", father);\n", - " auto girl = std::make_shared<Child2>(\"Leia\", father); \n", + " auto father = std::make_shared<Father>(\"Darth Vader\"); \n", + " auto boy = std::make_shared<Child>(\"Luke\", father);\n", + " auto girl = std::make_shared<Child>(\"Leia\", father); \n", " \n", " father->AddChild(boy);\n", " father->AddChild(girl); \n", " \n", " {\n", - " auto mistake = std::make_shared<Child2>(\"Yoda\", father); // ages don't even match!\n", + " auto mistake = std::make_shared<Child>(\"Yoda\", father); // ages don't even match!\n", " father->AddChild(mistake);\n", " } // mistake is released here: it becomes out of scope, and children_ data attribute\n", " // is made of weak pointers.\n", @@ -314,7 +316,9 @@ "source": [ "This code causes an issue (which might be a crash or an error depending on your platform): the vector still includes `Yoda` as a child of `Darth Vader`, whereas the object no longer exists (we're once again in undefined behavior territory).\n", "\n", - "There is a method `expired()` that returns `true` if the underlying object no longer exists; but in fact `lock()` gets a return value that may be used to the same purpose (see [@Coliru](http://coliru.stacked-crooked.com/a/fcdb5cf21897e4ef)):" + "There is a method `expired()` that returns `true` if the underlying object no longer exists; but in fact `lock()` gets a return value that may be used to the same purpose.\n", + "\n", + "We just change the `Print()` function (see [@Coliru](http://coliru.stacked-crooked.com/a/c9deaf942e703f4c)):" ] }, { @@ -323,6 +327,8 @@ "metadata": {}, "outputs": [], "source": [ + "// Doesn't work in Xeus-cling!\n", + "\n", "void Father::Print() const\n", "{\n", " std::cout << name_ << \" is the father of \" << children_.size() << \" children: \" << std::endl;\n", @@ -335,29 +341,6 @@ "}" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "{\n", - " auto father = std::make_shared<Father2>(\"Darth Vader\"); \n", - " auto boy = std::make_shared<Child2>(\"Luke\", father);\n", - " auto girl = std::make_shared<Child2>(\"Leia\", father); \n", - " \n", - " father->AddChild(boy);\n", - " father->AddChild(girl); \n", - " \n", - " {\n", - " auto mistake = std::make_shared<Child2>(\"Yoda\", father); // ages don't match!\n", - " father->AddChild(mistake);\n", - " }\n", - " \n", - " father->PrintWithLock();\n", - "}" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -371,7 +354,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "````\n", + "```\n", "Allocating father Darth Vader\n", "Allocating child Luke\n", "Allocating child Leia\n", @@ -383,7 +366,7 @@ "Release Child Leia\n", "Release Child Luke\n", "Release Father Darth Vader\n", - "````" + "```" ] }, {