I understand benefits of using exceptions handling in C++ and I'm aware that it may be tricky. One of rule says, that every function may throw. Ok, but there are situations, where we want to make sure, that function does not throw. I'm looking for any well-known practices or guidelines for handling such situations. Examples:
try
{
// do something
}
catch (std::runtime_error& error)
{
save_log (error);
emit_dbus_signal (error);
}
I don't care if save_log() or emit_dbus_signal() will fail, I only want to make sure, that I tried to call them.
ThreadPool thread_pool;
SocketsPool socket_pool;
MainLoop main_loop;
try
{
thread_pool.init ();
socket_pool.init ();
main_loop.run ();
}
catch (std::runtime_error& error)
{
save_log (error);
emit_dbus_signal (error);
}
thread_pool.finalize ();
socket_pool.finalize ();
I only want to make sure, that I tried to finalize thread_pool and socket_pool. Any error during finalization process should be handled inside finalize() methods.
I can remember, which functions does not throw, but it will work only for small programs. Should I add suffix like _nothrow to such "non throwing" functions' names and handle this while writing code? Exceptions specification is deprecated since C++11 so I want to avoid it. What about noexcept? I still not sure if I understand this new feature. Is it what I'm looking for?
There's no excpetions checking at compile time in C++11 right?
Or maybe I'm completely wring? :)
I think you need to understand RAII as a starting point.
Once you're using RAII correctly, you'll find that most of the case where you thought you needed to manually finalize objects have magically disappeared: And, so in a lot of cases, that means you can dispense with the try/catch/finalize approach entirely.
As others have said, you're still going to want your save_log/emit_dbus_signal calls in a catch statement somewhere...
In the above case, ThreadPool's constructor would call init(), and the destructor would call finalize().
You should certainly document which functions are guaranteed not to throw, don't just "remember"!
A common example is that swap functions should be no-throw. In that case, there's no reason to put nothrow in the name, it's fairly fundamental that many uses of swap need to be nothrow. You could likewise make a rule for your project that log functions never throw. But this still needs to be documented. Ideally, every function should document what it throws and why, but failing that every function should certainly document what level of exception guarantee it offers, and "nothrow" is the strongest level.
If I had throwing and non-throwing versions of the same functionality, then personally I'd put nothrow in the name to distinguish them. Other than that, see what the code looks like. It's possible you'll find yourself writing a piece of code in which you call seven functions in a row, all of which have to be nothrow for the code to be correct. It would probably be helpful to future readers not to have to go and check the declaration of each of those functions to make sure it really doesn't throw, although IDEs help with that. They certainly don't want to have to read 7 doc files if that's avoidable. In that case, I suppose it might be helpful to have Hungarian-style warts in the function names, but that kind of thing can rapidly get out of hand and make the code harder to read, not easier.
Also, if you use a naming convention then operator overloads become rather difficult - you can't distinguish between a throwing and non-throwing operator+ by name.
Empty exceptions specifications are OK, and C++11 noexcept is probably better. Aside from their meaning to the compiler, they help with documentation. It's non-empty exception specifications that are troublesome.
I agree with what everyone is saying about finalize: that's what destructors are for.
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