(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Ecl Exception Handling

Description: Introduction to the usage of exceptions in the ecl.

Keywords: ecl exceptions

Tutorial Level: INTERMEDIATE

Exception Types

Standard Exceptions

ecl::StandardException fulfills the general exception type used by the ecl. Note we're just using one class for most exceptions - typically c++ encourages throwing different classes of exceptions for different error types, and we'll probably upgrade to that sometime. Until now though, this has proved conveniently practical and we haven't found any good reason to make things more complicated yet.

Anywhere you wish to throw an ecl::StandardException, throw it by supplying the exception constructor some important information at the point of failure (maybe naive!? if so, please enlighten!).

  • LOC, the code location macro stamp defined in ecl_errors.

  • One of the ErrorFlag enum values defined in ecl_errors.

  • Optionally an extra string customised to provide further detail.

   1 void f(int i) throw(StandardException) {
   2     if ( i > 5 ) { 
   3         throw StandardException(LOC,InvalidInputError,"The maximum input is 5.");
   4     }   
   5 }

Typical output from this will be of the form:

Location : /mnt/froody/work/code/cpp/projects/ecl/modules/core/src/tests/exceptions.cpp:154
         : /mnt/froody/work/code/cpp/projects/ecl/modules/core/src/tests/exceptions.cpp:151
Flag     : There was a configuration error.
Detail   : Intentionally throwing in debug mode.

If you wish to manually catch and handle them, then you can implement a handler as follows.

   1 try {
   2     f(6);
   3 } catch ( StandardException &e) {
   4     cout << e.what() << endl;
   5     if ( e.flag() == ConfigurationError ) {
   6         // handle the error here.
   7     }
   8 }

Note the use of the flag handler within the if statement.


Another variant is the data exception which allows an additional data object to be bundled with the exception. This is a templatised class. Note, if you're going to output the error message, the data object needs to be streamable to the standard output (if not, you will get a compile time error).

   1 try {
   2     throw DataException<int>(LOC, ConfigurationError,"Throwing a data exception with an int.", 7);
   3 } catch ( DataException<int> &e ) {
   4     std::cout << "Data: " << e.data() << std::endl;
   5     std::cout << e.what() << std::endl;
   6 }

Rethrowing Exceptions

You can also rethrow a StandardException, useful if you want to track the source of the exception as it goes back up the program heirarchy. This is done by passing the old exception to the constructor call of a new throw.

   1 void f(int i) throw(StandardException) {
   2     if ( i > 5 ) {
   3         throw StandardException(LOC,InvalidInputError,"The maximum input is 5.");
   4     }
   5 }
   7 int main() {
   8     try {
   9         f(6);
  10     } catch (StandardException &e) {
  11         cout << e.what() << endl;
  12         throw StandardException(LOC,e);  // Rethrow with a new LOC
  13     }
  14 }

Disabling Exceptions

Disabling All Exceptions

Exceptions in any of the ecl libraries can be compiled out by simply passing the -DECL_DISABLE_EXCEPTIONS to cmake when compiling the ecl libraries (not your program!). If building in ros you can set this variable in $ROS_ROOT/rosconfig.cmake. Note that this variable is picked up by ecl_config where it sets a macro that is used throughout by the ecl libraries.

When you disable ecl exceptions, all ecl classes are designed in such a way so that they can gracefully fall back to a lower form of error handling.

Debug Only Exceptions

Exceptions can also be enabled exclusively for debug mode (so long as NDEBUG is not defined) with the debug_throw and debug_throw_decl macros. When NDEBUG is defined, the throw will automatically disappear from the program code.

   1 void f(int i) ecl_debug_throw_decl(StandardException) {
   2     if ( i > 5 ) {
   3         ecl_debug_throw( StandardException(LOC,InvalidInputError,"The maximum input is 5.") );
   4     }
   5 }

Catching can be done with the usual try-catch blocks, but if you want to pre-process this code away as well, you can use the debug_try and debug_catch macros.

   1 ecl_try {
   2     std::cout << "Try" << std::endl;
   3     ecl_throw(ecl::StandardException(LOC,ecl::OpenError));
   4 } ecl_catch(ecl::StandardException &e) {
   5     std::cout << "Catch" << std::endl;
   6 }

Assertive Exceptions

In addition, you may use the assert_throw and assert_throw_decl macros (a mechanism similar to the run_time_assert function in ecl_errors) as follows:

   1 void f(int i) ecl_assert_throw_decl(StandardException) {
   2     ecl_assert_throw( i <= 5, StandardException(LOC,InvalidInputError,"The maximum input is 5.") );
   3 }

Wiki: ecl_exceptions/Tutorials/Ecl Exception Handling (last edited 2015-02-05 00:48:45 by DanielStonier)