Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 exception design. Idioms and best practices changed?

Tags:

I am wondering if, since C++11, where passing exceptions between threads was added and nested exceptions were added, idioms changed for exception capturing, in general.

Now we have:

  1. std::rethrow_if_nested
  2. std::rethrow_with_nested
  3. std::rethrow_exception
  4. std::current_exception

Nested exceptions are supposed to be used to not lose context for exceptions.

So now you can do something like this:

void open_file(std::string const & file_name) {
   try {
      std::ifstream file;
      file.exceptions(ios::failbit | ios::badbit);
      file.open(file_name);
   }
   catch (...) {
      std::rethrow_with_nested(std::logic_error("File " + file_name + 
      " could not be open"));
   }
}

You can get the backtrace like this, if I'm not wrong:

void print_backtrace(std::exception const & e, int depth = 0) {
   std::cerr << std::string(depth, ' ') << e.what() << std::endl;
   try {
      std::rethrow_if_nested(e);
   }
   catch (std::exception const & ex) {
      print_backtrace(ex, ++depth);
   }
}

So if you use print_backtrace with open_file it should give you the std::logic_error + the ios_base::failure in the output.

My questions are:

  1. Is this idiom the "correct" way of handling exceptions in c++11, given that I want to capture all exceptions without losing context?
  2. Is there a way in the print_backtrace function to capture exceptions with catch (...) to capture absolutely all?
  3. I don't know why std::rethrow_exception is needed and I don't know when either.
like image 866
Germán Diago Avatar asked Nov 17 '13 03:11

Germán Diago


People also ask

Is exception handling in C++ cheap?

As a rule of thumb, exception handling is extremely cheap when you don't throw an exception. It costs nothing on some implementations. All the cost is incurred when you throw an exception: that is, “normal code” is faster than code using error-return codes and tests.

Should I use exceptions in C++?

Exceptions are preferred in modern C++ for the following reasons: An exception forces calling code to recognize an error condition and handle it. Unhandled exceptions stop program execution. An exception jumps to the point in the call stack that can handle the error.

Which is used to prevent a function from throwing any type of exceptions?

Explanation: C++ uses catch block to handle any exceptions that occur during run-time of the program.

What will happen if thrown exception is not handled in C++?

Explanation: As the func() is throwing a const char* string but we the catch block is not catching any const char* exception i.e. exception thrown is not handled therefore the program results into Aborted(core dumped).


1 Answers

1. I don't know that I'd call it an idiom. If by 'the "correct"' you mean something similar to how std::vector is the 'correct' container to use by default, I don't think there really is a particular "correct" way of handling errors. This is a correct way in that it's well defined behavior.

2. First you have to call print_backtrace() in a context that's not limited to certain exceptions, which means you have to call it in a catch(...) block:

    try {
      run();
    } catch(...) {
      print_backtrace();
    }

But then you don't have an exception of a known type to pass along to the function. Instead you have to write the function to access the exception differently; by throwing that exception and catching it internally (since this is the only mechanism by which you can bind a variable of a known type to an arbitrary exception).

void print_backtrace(int depth = 0) {
  try {
    throw;
  }

  // this block shows how to handle exceptions of some known type
  // You can have your own types instead of std::exception
  catch (const std::exception & e) {
    std::cerr << std::string(depth, ' ') << e.what() << std::endl;
    try {
      std::rethrow_if_nested(e);
    }
    catch (...) {
      print_backtrace(++depth);
    }
  }

  // Not all nesting exceptions will be of a known type, but if they use the
  // mixin type std::nested_exception, then we can at least handle them enough to
  // get the nested exception:

  catch (const std::nested_exception & ne) {
    std::cerr << std::string(depth, ' ') << "Unknown nesting exception\n";

    try {
      ne.rethrow_nested();
    }
    catch (...) {
      print_backtrace(++depth);
    }
  }

  // Exception nesting works through inheritance, which means that if you
  // can't inherit from the type, then you can't 'mixin' std::nesting exception.
  // If you try something like std::throw_with_nested( int{10} ); Then you'll
  // hit this catch block when printing the backtrace.

  catch (...) {
    std::cerr << std::string(depth, ' ') << "Unknown exception\n";
  }
}

3. std::rethrow_exception is used with std::exception_ptr, which is a type that can be used to transport an arbitrary exception. The only way to get at that exception is to use the normal exception handling machinery to catch the exception, which means you have to be able to throw that exception. That's what rethrow_exception does. This can be used to transport an arbitrary exception from one thread to another (as std::future does), or to hold an arbitrary exception as a member (as std::nested_exception does), or to pass an arbitrary exception as a parameter to a function which will try to print some description of the exception.

void print_backtrace(std::exception_ptr e) {
  try {
    std::rethrow_exception(e);
  }

  catch (const std::exception & e) {
    // ...
like image 68
bames53 Avatar answered Oct 12 '22 01:10

bames53