diff --git a/5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb b/5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb index 396250402cf40192e8fdd232f1e632f6b2a056ff..076b4c79c46a15e9d8e1b3a51b401de6c1aae71b 100644 --- a/5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb +++ b/5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb @@ -27,7 +27,7 @@ "\n", "## Compiler warnings and errors\n", "\n", - "The first way to find out possible errors are during compilation time: you may ensure your code is correct by making its compilation fails if not. There are many ways to do so, even more so if templates are involved; here are few of them we have already seen:\n", + "The first way to find out possible errors are during compilation time: you may ensure your code is correct by making its compilation fails if not (that's exactly the spirit of the example we provided for [template template parameter](../4-Templates/5-MoreAdvanced.ipynb#Template-template-parameters-(not-a-mistake...)). There are many ways to do so, even more so if templates are involved; here are few of them we have already seen:\n", "\n", "* `static_assert` we saw in [template introduction](../4-Templates/1-Intro.ipynb#static_assert)\n", "* Duck typing failure: if a template argument used somewhere doesn't comply with the expected API.\n", @@ -71,7 +71,7 @@ "\n", "So you may get extensive tests in debug mode that are ignored once your code has been thoroughly checked and is ready for production use (_debug_ and _release_ mode will be explained in a [later notebook](../6-InRealEnvironment/3-Compilers.ipynb#Debug-and-release-flags)).\n", "\n", - "The example above is a very useful use: before dereferencing a pointer check it is not `nullptr` (hence the good practice to always initialize a pointer to `nullptr`...)\n", + "The example above is a very useful use: before dereferencing a pointer checks it is not `nullptr` (hence the good practice to always initialize a pointer to `nullptr`...)\n", "\n", "In **release mode**, the macro `NDEBUG` should be defined and all the `assert` declarations will be ignored by the compiler.\n", "\n", @@ -97,7 +97,7 @@ "source": [ "Asserts are clearly a **developer** tool: they are there to signal something does not behave as intended and therefore that there is a bug somewhere...\n", "\n", - "However, they are clearly not appropriate to handle an error of your program end-user: for instance if he specifies an invalid input file, you do not want an abort which is moreover handled only in debug mode!\n", + "However, they are clearly not appropriate to handle an error of your program end-user: for instance if he specifies an invalid input file, you do not want an `abort` which is moreover handled only in debug mode!\n", "\n", "### `throw`\n", "\n", @@ -178,9 +178,9 @@ " catch(int n)\n", " {\n", " if (n == 1)\n", - " std::cout << \"Error: value is bigger than 9!\" << std::endl;\n", + " std::cerr << \"Error: value is bigger than 9!\" << std::endl;\n", " if (n == -1)\n", - " std::cout << \"Error: value is less than -9!\" << std::endl;\n", + " std::cerr << \"Error: value is less than -9!\" << std::endl;\n", " }\n", " std::cout << \"End\" << std::endl;\n", "}" @@ -206,11 +206,11 @@ " }\n", " catch(float n) // doesn't catch your `int` exception!\n", " {\n", - " std::cout << \"Float case: \" << n << \" was provided and is not an integer\" << std::endl;\n", + " std::cerr << \"Float case: \" << n << \" was provided and is not an integer\" << std::endl;\n", " }\n", " catch(...)\n", " {\n", - " std::cout << \"Gluttony case... but no object to manipulate to extract more information!\" << std::endl;\n", + " std::cerr << \"Gluttony case... but no object to manipulate to extract more information!\" << std::endl;\n", " }\n", "}" ] @@ -221,7 +221,7 @@ "source": [ "### Re-throw\n", "\n", - "Once an exception has been caught by a `catch` block, it is considered to be handled; the code will therefore go on to what is immediately after the block. If you want to throw the exception again (for instance afterloggin a message) you may just type `throw`:" + "Once an exception has been caught by a `catch` block, it is considered to be handled; the code will therefore go on to what is immediately after the block. If you want to throw the exception again (for instance after logging a message) you may just type `throw`:" ] }, { @@ -238,7 +238,7 @@ " }\n", " catch(int n)\n", " {\n", - " std::cout << \"Int case: \" << n << \" not a single digit number\" << std::endl;\n", + " std::cerr << \"Int case: \" << n << \" not a single digit number\" << std::endl;\n", " }\n", " \n", " std::cout << \"After catch\" << std::endl;\n", @@ -259,7 +259,7 @@ " }\n", " catch(int n)\n", " {\n", - " std::cout << \"Int case: \" << n << \" not a single digit number\" << std::endl;\n", + " std::cerr << \"Int case: \" << n << \" not a single digit number\" << std::endl;\n", " throw; // `throw n` would have been correct as well but is not necessary\n", " }\n", " \n", @@ -345,7 +345,7 @@ " }\n", " catch(const std::exception& e)\n", " {\n", - " std::cout << \"Properly caught: \" << e.what() << std::endl;\n", + " std::cerr << \"Properly caught: \" << e.what() << std::endl;\n", " }\n", "}" ] @@ -365,7 +365,7 @@ "metadata": {}, "outputs": [], "source": [ - "// Does not run in Xeus-cling!\n", + "// Pseudo-code - Do not run in Xeus-cling!\n", "\n", "try\n", "{\n", @@ -396,6 +396,8 @@ "* It is rather time consuming: defining a specific exception means a bit of boilerplate to write, and those minutes might have been spent more efficiently, as...\n", "* Most of the time, you don't even need the filtering capacity; in my code for instance if an exception is thrown it is 99 % of the time to be caught in the `main()` function to terminate properly the execution.\n", "\n", + "The only case in which it might be very valuable to use a tailored exception is for your integration tests: if you are writing a test in which an exception is expected, it is better to check the exception you caught is exactly the one that was expected and not a completely unrelated exception which was thrown for another reason.\n", + "\n", "STL provides many derived class from `std::exception` which you might use directly or as base of your own class; see [cppreference](https://en.cppreference.com/w/cpp/error/exception) for more details. [OpenClassrooms](https://openclassrooms.com/fr/courses/1894236-programmez-avec-le-langage-c/1903837-gerez-des-erreurs-avec-les-exceptions) (which despite its name is in french) sorted out the go-to exceptions for lazy developers which cover most of the cases (don't get me wrong: laziness is often an asset for a software developer!):\n", "\n", "* `std::domain_error`\n", @@ -492,7 +494,7 @@ "I don't like these error codes much, because:\n", "\n", "* The result can't be naturally given in return value and must be provided in argument.\n", - "* You have to bookkeep the possible error codes somewhere, and a user must know this somewhere to go consult them if something happens (usually a header file: see for instance one for [PETSc library](https://www.mcs.anl.gov/petsc/petsc-master/include/petscerror.h.html)).\n", + "* You have to bookkeep the possible error codes somewhere, and an user must know this somewhere to go consult them if something happens (usually a header file: see for instance one for [PETSc library](https://www.mcs.anl.gov/petsc/petsc-master/include/petscerror.h.html)).\n", "* In the libraries that use them, more often than not some are not self descriptive and you have to figure out what the hell the issue is.\n", "* And more importantly, this relies on the end-user thinking to check the error value:" ] @@ -513,13 +515,20 @@ "}" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It should be noticed C++ 11 introduced a dedicated class to handle more gracefully error codes: [`std::error_code`](https://en.cppreference.com/w/cpp/error/error_code). I have no direct experience with it but it looks promising as illustrated by this [blog post](https://akrzemi1.wordpress.com/2017/07/12/your-own-error-code/)." + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "### nodiscard\n", "\n", - "The latter point may however be mitigated since C++17 with the attribute [nodiscard](https://en.cppreference.com/w/cpp/language/attributes/nodiscard), which helps your compiler figure out the return value should have been checked." + "The point about forgetting to check the value may however be mitigated since C++17 with the attribute [``nodiscard``](https://en.cppreference.com/w/cpp/language/attributes/nodiscard), which helps your compiler figure out the return value should have been checked." ] }, { @@ -637,5 +646,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 }