I'm implementing callbacks in C++ which will be called from ordinary C code. My main() function is C++ already, but C code will be responsible for creating threads that will eventually call my callbacks.
Right now my callbacks look like
int handle_foo(void *userdata) {
try {
MyCPPClass *obj = static_cast<MyCPPClass *>(userdata);
obj->doStuff();
return 0; // no error
} catch(...) {
LogError("doStuff failed");
return -1; // error
}
}
This works OK, but it seems weird to me. Furthermore, I lose some useful features such as the ability to find out what was thrown (without adding huge amounts of extra catch
statements to each and every one of my callbacks).
Is try {} catch(...) {}
here reasonable, or is there a better way to write my C callbacks?
Yes, you have to catch the exceptions and hopefully translate them into something useful. Letting exceptions propagate through C code leads to undefined behavior. At best you cannot expect the C code to maintain consistent program state.
See this answer for an easy example. A harder example is with some complex piece of software such as SQLite - C code will grab some mutex and will not release it because the exception simply "flies through" and your program is now toast.
Also this has any chance of "working" if all the code is built against the same C++ runtime. If you happen to have callback implemented in say Visual C++ 9 and the rest of the code in say Visual C++ 10 or those parts are compiled against static runtime libraries - you now have two distinct runtimes and the unhandled exception in the callback causes terminate()
being called.
sharptooth answered most of your question, and James McNellis has written a nice blog post about how to elegantly get rid of the boilerplate using modern C++.
The gist of it is having a translate_exceptions function used like this:
int callback(...) {
return translate_exceptions([&]{
// ... your code here
});
}
The translate_exception is a simple function template (which uses a function level try block) taking an invokable:
template<typename Fn> int translate_exception(Fn fn) try {
fn();
return 0;
} catch(std::exception const& e) {
LOG("[EE] exception ", e.what());
return -2;
} catch( ... ) {
LOG("[EE] unknown exception");
return -1;
}
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