layout: true class: animated fadeIn middle numbers .footnote[ `PSA` - N. Dubray - ENSIIE - 2018 - [:book:](../index.html) ] --- # Errors and exceptions .shadow.hcenter.w45[] .hcenter[[source: thomaskausch.wordpress.com](https://thomaskausch.wordpress.com/2017/07/13/nil-null-nsnull-optionals/)] --- # Errors and exceptions ## Runtime errors :arrow_right: when a program encounters unusual execution conditions, errors can happen **at runtime**. ## Examples: external causes * the system runs out of memory, * a file can not be opened because it has been deleted, * hardware error, * etc... ## Examples: internal causes * a division by zero occurs during the evaluation of an expression, * "double free", * **error-prone implementation**, * etc... ## Severity level * a **benign error** should not perturb the program final output, should spawn an **info message**, * a **small error** may perturb the program final output, should spawn a **warning message**, * a **serious error** will perturb the program final output, should spawn an **error message**, * a **disastrous error** prevents the program from continuing, should spawn a .red.bold[critical error message]. --- # Errors and exceptions ## Possible ways to react to a runtime error 1. **do nothing**: the program will have an **unexpected behavior** (result OK, segfault, etc...), 2. print an error message on `stderr` and exit, 3. set a flag or return an **error code**, try to continue the program execution if possible. :warning: runtime errors will not always cause visible effects. ## Common misconception .alert[ If modifying a "working" program leads to a bad behavior, then **there must be a bug in the modified parts of the code**. ] :warning: This deduction is **NOT ALWAYS TRUE**. * some runtime errors have no visible side effect until a different portion of the code is reached. * some runtime errors have no visible side effect until a change in the environment occurs. * **you never know when a program is "working"**. ## Golden rules :arrow_right: consider that no program is bug-free, :arrow_right: remember that runtime errors may have no side effects **for a long time** before crashing a program. --- # Errors and exceptions ## Error codes :arrow_right: returning an error code in case of a runtime error may seem to be a good solution, **but** * each library may define its own error codes, * every function call should be followed by a test to know if an error happened, to break the program's flow, * the return value of a function may be used to do something else than returning error codes, * separating the handling of runtime errors and the implementation is hard. ## `C++` Exceptions :arrow_right: provide a **standard** mechanism to handle runtime errors. :+: very important when mixing libraries, :+: allow to break the program's flow on runtime errors, :warning: may induce an overhead if exceptions are frequent. .vspace[] .hcenter[.red.bold[:warning:Exceptions in `C++` should be used for "exceptional" conditions.:warning:]] :arrow_right: do not throw exceptions for parsing errors, invalid function parameters, etc... --- # Exception handling in `C++` ## The four parts of exception handling mechanism 1. Identify a runtime problem (**hit** the exception) 2. Inform your calling function (**throw** the exception) 3. Receive the error information (**catch** the exception) 4. Decide what to do (**handle** the exception) ## `try` :arrow_right: defines a **code block** from which some exceptions should be catched. ## `throw` :arrow_right: throw an exception of a given type, then jump to the **first corresponding `catch` block** (if any). ## `catch` :arrow_right: defines a **code block** to handle a given exception type thrown from the `try` code block. :warning: whenever possible (always ?), `catch` **references**. --- class: top # Exception handling in `C++` ## .hcenter[\[example0.cpp\]] ```C++ double func0(int a, int b) { if (b == 0) // hit { throw b; // throw } } try { double v0 = func0(3, 1); double v1 = func0(2, 0); double v2 = func0(5, 0); } catch(int &a) // catch { std::cout << "division by zero error" << std::endl; // handle } ``` -- ## .hcenter[Shell session] ```shell $ g++ example0.cpp -o example0 $ ./example0 division by zero error ``` --- # Exception handling in `C++` ## Catch several exceptions :arrow_right: provide several `catch` blocks with different types. ```C++ try { do_something(); } catch(int &i) { handle0(); } *catch(double &v) { handle1(); } *catch(std::string &s) { handle2(); } ``` :arrow_right: allows to handle different exceptions differently. ## Catch all exceptions :arrow_right: provide a special `catch(...)` block. ```C++ try { do_something(); } catch(int &i) { handle0(); } catch(double &v) { handle1(); } catch(std::string &s) { handle2(); } *catch(...) { handle3(); } ``` :arrow_right: can be used as a "default" catch. --- # Exception handling in `C++` ## Exception rethrowing :arrow_right: when throwing an exception from a `catch` block. :arrow_right: simply use `throw` to rethrow the exception. ```C++ try { do_something0(); try { do_something1(); } catch(myType &a) { handle0(); * throw; } } catch(myType &b) { handle1(); } ``` --- class: top # Exception handling in `C++` ## .hcenter[\[example1.cpp\]] ```C++ #include
double func0(int a, int b) { if (a == 0) throw 3; return 0.0; } double func1(int a, int b) { double r = 0.0; try { r = func0(0, b); std::cout << "normal execution" << std::endl; } catch(int &i) { std::cout << "catch in func1" << std::endl; * throw; } return r; } int main(int argc, char ** argv) { try { func1(0,1); } catch(int &i) { std::cout << "catch in main" << std::endl; } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example1.cpp -o example1 $ ./example1 catch in func0 catch in main ``` --- # Exception handling in `C++` ## Restricting exception types :arrow_right: it is possible to specify the exception types a function can throw. :warning: this mechanism has been **deprecated** since `C++11` (still supported though). ```C++ int func0(int a, int b) throw() // all exceptions from this function will trigger std::unexpected. { [...] } int func1(int a, int b) throw(int, char) // all exceptions from this function, except int and char ones, will trigger std::unexpected. { [...] } ``` ## Customize `std::unexpected` ```C++ #include
#include
void customUnexpected(void) { std::cerr << "unexpected called" << std::endl; throw 0; // must throw something or terminate the program } int main (void){ { * std::set_unexpected(customUnexpected); [...] } ``` --- class: top # Exception handling in `C++` ## .hcenter[\[example2.cpp\]] ```C++ #include
*double func0(int a, int b) throw(int, char, double) { if (b == 0) // hit { throw 'b'; // throw a char } return (double)a / (double)b; } int main(int argc, char ** argv) { try { double v1 = func0(2, 0); } catch(char &c) // catch { std::cout << "catch char exception" << std::endl; // handle } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example2.cpp -o example2 $ ./example2 catch char exception ``` --- class: top # Exception handling in `C++` ## .hcenter[\[example3.cpp\]] ```C++ #include
*double func0(int a, int b) throw(int, double) { if (b == 0) // hit { throw 'b'; // throw a char } return (double)a / (double)b; } int main(int argc, char ** argv) { try { double v1 = func0(2, 0); } catch(char &c) // catch { std::cout << "catch char exception" << std::endl; // handle } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example3.cpp -o example3 $ ./example3 *terminate called after throwing an instance of 'char' *Aborted ``` --- # Exception handling in `C++` ## The `noexcept` specifier (since `C++11`) :arrow_right: used to specify that a function should not throw exceptions. ```C++ int func0(int a, int b) noexcept; // can not throw int func1(int a, int b) noexcept(true); // can not throw int func2(int a, int b) noexcept(false); // can throw ``` :arrow_right: If a `noexcept` function throws any exception, it triggers `std::terminate`. --- class: top # Exception handling in `C++` ## .hcenter[\[example4.cpp\]] ```C++ #include
#include
*int func0(int a, int b) noexcept { throw 42; } int main(int argc, char ** argv) { try { func0(0, 0); } catch(int &i) { std::cout << "catch int exception" << std::endl; } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ --std=c++11 example4.cpp -o example4 *example4.cpp: In function 'int func0(int, int)': *example4.cpp:6:9: warning: throw will always call terminate() [-Wterminate] * throw 42; * ^~ $ ./example4 *terminate called after throwing an instance of 'int' *Aborted ``` --- # Exception handling in `C++` ## Standard exception types :arrow_right: derived from the `exception` class. :arrow_right: the `exception` class provides a `what()` method which returns a `const char *`. :warning: these types have been **deprecated** since `C++11` (still supported though). .row[ .w48.hcenter[ | Exception | Derived from | | :--------------------- | :-------------- | | `bad_alloc` | `exception` | | `bad_cast` | `exception` | | `bad_exception` | `exception` | | `bad_typeid` | `exception` | | `bad_function_call` | `exception` | | `bad_weak_ptr` | `exception` | | `logic_error` | `exception` | | `runtime_error` | `exception` | ] .w48.hcenter[ | Exception | Derived from | | :--------------------- | :-------------- | | `domain_error` | `logic_error` | | `future_error` | `logic_error` | | `invalid_argument` | `logic_error` | | `length_error` | `logic_error` | | `out_of_range` | `logic_error` | | `overflow_error` | `runtime_error` | | `range_error` | `runtime_error` | | `system_error` | `runtime_error` | | `underflow_error` | `runtime_error` | | `bad_array_new_length` | `bad_alloc` | | `ios_base::failure` | `system_error` | ] ] --- class: top # Exception handling in `C++` ## .hcenter[\[example5.cpp\]] ```C++ #include
#include
int main(int argc, char ** argv) { try { int* myarray= new int[10000000000]; } * catch(std::exception &e) { * std::cout << "catch exception, what(): " << e.what() << std::endl; } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example5.cpp -o example5 $ ./example5 catch exception, what(): std::bad_alloc ``` --- # Exception handling in `C++` ## Custom exception type :arrow_right: create your own exception class derived from `std::exception`. :arrow_right: if needed, overload `what()` and the constructor. ```C++ class myException: public std::exception { public: myException(std::string _mesg = "") { mesg = _mesg; } const char * what(void) { return mesg.c_str(); } std::string mesg; } ``` --- class: top # Exception handling in `C++` ## .hcenter[\[example6.cpp\]] ```C++ #include
#include
class myException: public std::exception { public: myException(std::string _mesg = "") { mesg = _mesg; } const char * what(void) { return mesg.c_str(); } std::string mesg; }; int main(int argc, char ** argv) { try { throw myException("custom text"); } catch(myException &e) { std::cout << "catch custom exception, what(): " << e.what() << std::endl; } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example6.cpp -o example6 $ ./example6 catch custom exception, what(): custom text ``` --- class: top # Exception handling in `C++` ## .hcenter[\[example7.cpp\]] ```C++ #include
#include
#include
class A {}; class myException: public A, public std::runtime_error { public: myException(std::string _mesg = "") : std::runtime_error(_mesg) {}; }; int main(int argc, char ** argv) { try { throw myException("custom text"); } catch(myException &e) { std::cout << "catch custom exception, what(): " << e.what() << std::endl; } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example7.cpp -o example7 $ ./example7 catch custom exception, what(): custom text ``` --- class: top # Exception handling in `C++` ## Derived exception type :arrow_right: you can catch the derived exception type or one of its ancestors. -- ## .hcenter[\[example8.cpp\]] ```C++ #include
#include
#include
class A {}; class myException: public A, public std::runtime_error { public: myException(std::string _mesg = "") : std::runtime_error(_mesg) {}; }; int main(int argc, char ** argv) { try { throw myException("custom text"); } * catch(std::runtime_error &e) { std::cout << "catch std::runtime_error exception, what(): " << e.what() << std::endl; } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example8.cpp -o example8 $ ./example8 catch std::runtime_error, what(): custom text ``` --- class: top # Exception handling in `C++` :warning: if you mix `catch` blocks for derived exception types, catch any exception type **before** its ancestors. ## .hcenter[\[example9.cpp\]] ```C++ #include
#include
#include
class A {}; class myException: public A, public std::runtime_error { public: myException(std::string _mesg = "") : std::runtime_error(_mesg) {}; }; int main(int argc, char ** argv) { try { throw myException("custom text"); } * catch(std::runtime_error &e) { std::cout << "catch std::runtime_error, what(): " << e.what() << std::endl; } * catch(myException &e) { std::cout << "catch custom exception, what(): " << e.what() << std::endl; } return 0; } ``` -- ## .hcenter[Shell session] ```shell $ g++ example9.cpp -o example9 *example9.cpp: In function 'int main(int, char**)': *example9.cpp:23:3: warning: exception of type 'myException' will be caught * catch(myException &e) * ^~~~~ *example9.cpp:19:3: warning: by earlier handler for 'std::runtime_error' * catch(std::runtime_error &e) * ^~~~~ $ ./example9 catch std::runtime_error, what(): custom text ``` --- # Exception handling in `Python` :arrow_right: exceptions in `Python` are **almost identical** as exceptions in `C++`. .row[ .w48[ ## .hcenter[`C++`] ```C++ try { do_something0() } catch (int a) { handle0(a); } catch (double b) { handle1(b); throw; } catch (...) { handle2(); } do_something1() ``` ] .w48[ ## .hcenter[`Python`] ```Python try: do_something0() except int as a: handle0(a) except float as b: handle1(b) raise except: handle2() do_something1() ``` ] ] --- class: top # Exception handling in `Python` ## Create a custom exception class :arrow_right: derivate from one of `Python` error classes. ## .hcenter[\[example10.py\]] ```Python #!/usr/bin/env python class myLookupErrorClass(LookupError): '''raise this class in case of a lookup error''' try: raise myLookupErrorClass('custom message') except myLookupErrorClass as e: print("catch myLookupErrorClass: %s" % (str(e))) except: print("generic catch") ``` -- ## .hcenter[Shell session] ```shell $ python example10.py catch myLookupErrorClass: custom message ``` -- :warning: Don't raise generic exceptions, you may miss specific ones. ```Python def demo_bad_catch(): try: raise ValueError('Represents a hidden bug, do not catch this') raise Exception('This is the exception you expect to handle') except Exception as error: print('Caught this error: ' + repr(error)) >>> demo_bad_catch() Caught this error: ValueError('Represents a hidden bug, do not catch this',) ``` --- # Exception handling in `Python` ## .hcenter[Default exceptions 1/2] .vspace[] .row[ .alert.hcenter.tree[ BaseException * SystemExit * KeyboardInterrupt * GeneratorExit * Exception * StopIteration * StandardError * [...] * Warning * [...] ] .block.hcenter.tree[ Warning * DeprecationWarning * PendingDeprecationWarning * RuntimeWarning * SyntaxWarning * UserWarning * FutureWarning * ImportWarning * UnicodeWarning * BytesWarning ] ] --- # Exception handling in `Python` ## .hcenter[Default exceptions 2/2] .row[ .block.hcenter.tree[ StandardError * BufferError * ArithmeticError * FloatingPointError * OverflowError * ZeroDivisionError * AssertionError * AttributeError * EnvironmentError * IOError * OSError * WindowsError (Windows) * VMSError (VMS) * EOFError * ImportError * LookupError * IndexError * KeyError ] .block.hcenter.tree[ StandardError * MemoryError * NameError * UnboundLocalError * ReferenceError * RuntimeError * NotImplementedError * SyntaxError * IndentationError * TabError * SystemError * TypeError * ValueError * UnicodeError * UnicodeDecodeError * UnicodeEncodeError * UnicodeTranslateError ] ] --- # Exception handling in `Python` ## Special exception :arrow_right: `KeyboardInterrupt` is raised when `SIGINT` is received. :warning: this is not the case in `C++`. ## .hcenter[\[example10.py\]] ```Python #!/usr/bin/env python try: print('press Ctrl-C') while True: pass except KeyboardInterrupt: print('SIGINT received') ``` ## .hcenter[Shell session] ```shell $ python example11.py press Ctrl-C ^CSIGINT received ``` --- # Exception handling in `Python` ## Exception chaining in `Pyhton3` :arrow_right: change the exception type, :arrow_right: keep the traceback, :arrow_right: make a full exception chain if needed. :warning: **not compatible with `Python2`**. ```Python try: do_something() except KeyError as e: raise RuntimeError('custom message') from e ``` ## Workaround in `Python2` :arrow_right: it is possible to add information to an exception. ```Python class MyError(Exception): def __init__(self, message, cause): super(MyError, self).__init__(message + u', caused by ' + repr(cause)) self.cause = cause try: v = {}['a'] # will throw a KeyError exception except KeyError as e: raise MyError('failed', e) ``` --- # Exception handling in `Python` ## Deprecated exception raising syntaxes ```Python raise ValueError, 'message' ``` :arrow_right: valid in `Python2` only, deprecated in `Python3`. ```Python raise 'message' ``` :arrow_right: valid in very old `Python2` (<2.4), deprecated in recent `Python2` and `Python3`. .hcenter.shadow.w60[] --- # Exception handling in `C++` and `Python` .row[ .w48[ ## .hcenter[\[myclass.h\]] ```C++ #ifndef MYCLASS_H #define MYCLASS_H class Myclass { public: Myclass(int vala = 0); double func0(double x); // calculate x * x + a void print(int); int a; }; #endif ``` ] .w48[ ## .hcenter[\[myclass.cpp\]] ```C++ #include
#include "myclass.h" Myclass::Myclass(int vala) { a = vala; } double Myclass::func0(double x) { if (x < 0.0) { throw std::runtime_error("EXCEPTION !!!"); } return x * x + a; } void Myclass::print(int i) { std::cout << i << std::endl; } ``` ] ] --- # Exception handling in `C++` and `Python` .row[ .w48[ ## .hcenter[\[testmod3.i\]] ```C %module testmod3 %{ #include "myclass.h" %} %include "exception.i" %exception { try { $action } catch (const std::runtime_error& e) { SWIG_exception(SWIG_RuntimeError, e.what()); } catch (const std::invalid_argument& e) { SWIG_exception(SWIG_ValueError, e.what()); } catch (const std::out_of_range& e) { SWIG_exception(SWIG_IndexError, e.what()); } catch (...) { SWIG_exception(SWIG_RuntimeError, "unknown exception"); } } %include "myclass.h" ``` ] .w48[ ## .hcenter[\[setup.py\]] ```Python from setuptools import setup, Extension module1 = Extension('_testmod3', sources = ['testmod3.i', 'myclass.cpp'], swig_opts = ["-c++"]) setup (name = 'package_test', py_modules = ['testmod3'], version = '1.0', description = 'This is a test package', ext_modules = [module1]) ``` .vspace[] .w100.hcenter[] ] ] --- # Exception handling in `C++` and `Python` ## Compilation (`Setuptools`) ```shell *$ python setup.py build running build running build_py file testmod3.py (for module testmod3) not found file testmod3.py (for module testmod3) not found running build_ext building '_testmod3' extension swigging testmod3.i to testmod3_wrap.cpp swig -python -c++ -o testmod3_wrap.cpp testmod3.i myclass.h:9: Warning 314: 'print' is a python keyword, renaming to '_print' creating build creating build/temp.linux-x86_64-2.7 x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-l1RrwO/python2.7-2.7.14=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/include/python2.7 -c testmod3_wrap.cpp -o build/temp.linux-x86_64-2.7/testmod3_wrap.o cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++ x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-l1RrwO/python2.7-2.7.14=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/include/python2.7 -c myclass.cpp -o build/temp.linux-x86_64-2.7/myclass.o cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++ creating build/lib.linux-x86_64-2.7 x86_64-linux-gnu-g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-l1RrwO/python2.7-2.7.14=. -fstack-protector-strong -Wformat -Werror=format-security -Wl,-Bsymbolic-functions -Wl,-z,relro -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-l1RrwO/python2.7-2.7.14=. -fstack-protector-strong -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/testmod3_wrap.o build/temp.linux-x86_64-2.7/myclass.o -o build/lib.linux-x86_64-2.7/_testmod3.so ``` --- # Exception handling in `C++` and `Python` ## Installation ```shell $ python setup.py install --user ``` ## Usage ## .hcenter[\[test.py\]] ```Python #!/usr/bin/env python import sys import testmod3 a = testmod3.Myclass() try: a.func0(-2.0) except RuntimeError as e: print("runtime error '%s'" % (e)) except: print("other error: %s" % (sys.exc_info()[1])) print('normal program end') ``` ## .hcenter[Shell session] ```shell $ python test.py runtime error 'EXCEPTION !!!' normal program end ``` --- # Errors and exceptions ## Main thing to remember :arrow_right: **There are different ways to use exceptions**: * exceptions must be raised exceptionnaly, * exceptions may be used for a lot of things (type checking, communication, etc...), * every intermediate usage, * exceptions **must be ignored**. .noflex[ .rightf.animated.fadeInRight.wait1s.w30[ .fshadow[] ] ## What to catch ? * catch everything, * catch what you throw, * catch what you can handle, * catch nothing, * Obi-Wan Kenobi ] ## My golden rule * raise an exception if a runtime error must not be ignored. * `C++` and `Python` exception usages may be slightly different (philosophical reasons). * there are no widely accepted guidelines on the good usage of exceptions. ## Conclusion * use exceptions as you see fit.