Question:
Is using exceptions the proper way to terminate my program if all I want is to display an error message and close (accounting that I may be deep in the program)? Or can I just explicitly call something like exit(EXIT_FAILURE)
instead?
What I'm Currently Doing:
I'm working on a game project and am trying to figure out the best way to terminate the program in the case of an error that calls for such an action. For example, in the case the textures can't be loaded I display an error message and terminate the program.
I'm currently doing this with exceptions like so:
int main() { Game game; try { game.run(); } catch (BadResolutionException & e) { Notification::showErrorMessage(e.what(), "ERROR: Resolution"); return 1; } catch (BadAssetException & e) { Notification::showErrorMessage(e.what(), "ERROR: Assets"); return 1; } catch (std::bad_alloc & e) { Notification::showErrorMessage(e.what(), "ERROR: Memory"); return 1; } return 0; }
All but bad_alloc are my own defined exceptions derived from runtime_error.
I don't need any manual resource cleanup and I'm using std::unique_ptr for any dynamic allocation. I just need to display the error message and close the program.
Research/Alternatives to Exceptions:
I've looked up a lot of posts on SO and other places and have seen others say anything from don't use exceptions, to use exceptions but your using them wrong. I've also looked up explicitly calling something like exit().
Using exit() sounds nice but I read it won't go back through the call stack up to main cleaning everything up (if I can find this again I'll post the link). Additionally, according to http://www.cplusplus.com/reference/cstdlib/exit/ this should not be used if multiple threads are active. I do expect to be creating a second thread for a short time at least once, and an error could occur in that thread.
Not using exceptions was mentioned in some replies here in relation to games https://gamedev.stackexchange.com/questions/103285/how-industy-games-handle-their-code-errors-and-exceptions
Use exceptions was discussed here: http://www.quora.com/Why-do-some-people-recommend-not-using-exception-handling-in-C++
There are a number of other sources I've read but those were the most recent I looked at.
Personal Conclusion:
Due to my limited experience of working with error handling and using exceptions, I'm not sure if I'm on the right track. I've chosen the route of using exceptions based on the code I posted above. If you agree that I should tackle those cases with exceptions, am I using it correctly?
If you let the exception propagate all the way up to the main() method, the program will end. There's no need to call System. exit , just allow the exception to bubble up the stack naturally (by adding throws IOException ) to the necessary methods.
An exception is an issue (run time error) occurred during the execution of a program. When an exception occurred the program gets terminated abruptly and, the code past the line that generated the exception never gets executed. There are two types of exceptions in Java.
If an exception is not caught with catch , the thread in which the exception occurred will be terminated. If no non-daemon threads remain the JVM will terminate. That's the only way how an exception might terminate a JVM. If you catch an exception it will never cause a JVM termination.
It's generally considered good practice to let all exceptions propagate through to main
. This is primarily because you can be sure the stack is properly unwound and all destructors are called (see this answer). I also think it's more organised to do things this way; you always known where your program will terminate (unless the program crashes). It's also facilitates more consistent error reporting (a point often neglected in exception handling; if you can't handle the exception, you should make sure your user knows exactly why). If you always start with this basic layout
int main(int argc, const char **argv) { try { // do stuff return EXIT_SUCCESS; } catch (...) { std::cerr << "Error: unknown exception" << std::endl; return EXIT_FAILURE; } }
then you won't go far wrong. You can (and should) add specific catch
statements for better error reporting.
Exceptions when multithreading
There are two basic ways of executing code asynchronously in C++11 using standard library features: std::async
and std::thread
.
First the simple one. std::async
will return a std::future
which will capture and store any uncaught exceptions thrown in the given function. Calling std::future::get
on the future will cause any exceptions to propagate into the calling thread.
auto fut = std::async(std::launch::async, [] () { throw std::runtime_error {"oh dear"}; }); fut.get(); // fine, throws exception
On the other hand, if an exception in a std::thread
object is uncaught then std::terminate
will be called:
try { std::thread t {[] () { throw std::runtime_error {"oh dear"};}}; t.join(); } catch(...) { // only get here if std::thread constructor throws }
One solution to this could be to pass a std::exception_ptr
into the std::thread
object which it can pass the exception to:
void foo(std::exception_ptr& eptr) { try { throw std::runtime_error {"oh dear"}; } catch (...) { eptr = std::current_exception(); } } void bar() { std::exception_ptr eptr {}; std::thread t {foo, std::ref(eptr)}; try { // do stuff } catch(...) { t.join(); // t may also have thrown throw; } t.join(); if (eptr) { std::rethrow_exception(eptr); } }
Although a better way is to use std::package_task
:
void foo() { throw std::runtime_error {"oh dear"}; } void bar() { std::packaged_task<void()> task {foo}; auto fut = task.get_future(); std::thread t {std::move(task)}; t.join(); auto result = fut.get(); // throws here }
But unless you have good reason to use std::thread
, prefer std::async
.
There's nothing wrong with catching unrecoverable errors and shutting down your program this way. In fact, it's how exceptions should be used. However, be careful not to cross the line of using exceptions to control the flow of your program in ordinary circumstances. They should always represent an error which cannot be gracefully handled at the level the error occurred.
Calling exit()
would not unwind the stack from wherever you called it. If you want to exit cleanly, what you're already doing is ideal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With