Goofy
Goofy

Reputation: 5399

Exceptions handling guidelines for C++

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? :)

Upvotes: 3

Views: 1761

Answers (6)

edA-qa mort-ora-y
edA-qa mort-ora-y

Reputation: 31951

Add throw() to the end of a function declaration. The compiler (gcc at least) will then complain if the function throws exceptions.

void function() throw();

This is standard C++ that says this function doesn't throw any exceptions. Previously you could say which exceptions it threw, but I think C++11 removed that feature. Only the empty clause, as above, remains.

Upvotes: 0

Roddy
Roddy

Reputation: 68114

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().

Upvotes: 3

This depends on what you actually intend on doing. When you say that you do not care whether save_log or emit_dbus_signal fails, you are not saying what don't care means. That is, if save_log fails, do you want to still try and emit_dbus_signal? If so, you can:

catch ( std::runtime_error& error ) {
   try { save_log( error ); } catch (...) {}
   try { emit_dbus_signal( error ); } catcn ( ... ) {}
}

If you do not care about emit_dbus_signal not being called if save_log fails, another approach would be enclosing the whole try/catch inside a second try/catch:

try {
   try {
     // current code
   } catch ( std::runtime_error const & error ) {
     // current handling
   }
} catch (...) {} // ensure that no other exception escapes either the try or the catch blocks
thread_pool.finalize();
socket_pool.finalize();

There are actually other approaches, like using a RAII to ensure that threadpool.finalize() is called regardless of how the function completes in the lines of ScopeGuard.

Upvotes: 1

Steve Jessop
Steve Jessop

Reputation: 279455

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.

Upvotes: 3

Alan
Alan

Reputation: 1945

Read up on RAII http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

If your function throws, any stack variables in that function will still have their destructors called. That means you can simply call finalize() inside the ~ThreadPool() and ~SocketPool() destructors.

If you can't modify the ThreadPool or SocketPool classes, you can write your own wrapper classes that call finalize on destruction, e.g.

class ScopedThreadPool
{
public:
    ScopedThreadPool(ThreadPool &threadPool) : m_threadPool(threadPool) {}
    ~ScopedThreadPool() { m_threadPool.finalize(); }

private:
    ThreadPool &m_threadPool;
};

And then use it like this...

ThreadPool threadPool;
ScopedThreadPool scopedThreadPool(threadPool);

When the function exits (either via return or throw) then the ScopedThreadPool destructor will call finalize() for you.

Upvotes: 0

thiton
thiton

Reputation: 36059

For the finalization functions, you should consider using wrapper classes with the RAII principle instead, that is, rewrite your code using:

try {
    initialized_thread_pool pool = thread_pool.init();
} catch (std::runtime_error& error) { handle(error); }

and do the finalization in the destructor of initialized_thread_pool.

Once you get that principle right, exception specifications don't seem that much of an issue anymore.

Upvotes: 0

Related Questions