views:

287

answers:

3

Say I have nested methods a, b, c, d, e on each level we return errors in the normal course of operations, but e could also throw an exception (e.g. out of memory on STL insert). The exceptions are very seldom and how fast/slow actual unwinding is happening is not an issue.

What is the most appropriate strategy for exception handling in this case?

  1. Put it into the lowest level and convert to a normal error condition.

Pros: do not need to write exception safe code, simplest to implement, easiest to test, easiest to understand, minimum compile time information required for unwinding.

Cons: does not look cool, adds noticeable try/catch clatter - practically around every insert and push_back, up to the extent of writing exception safe wrappers around STL containers, there are opinions that there is run time performance penalty for try blocks (and there are opinions that there is no penalty at all).

  1. Handle it it at the top.

Pros: looks cool, no clatter.

Cons: really hard to visually verify that all code in between is indeed exception safe to testing all exception unwinding paths will be

  1. Handle it the very top as a complete restart of the application: zap everything that was not zapped by exception handling and start again

Pros: predictable, will tolerate minor blemishes in exception safe code, way better than crash.

Cons: way too harsh

  1. Writing custom allocator, allowing to check memory reserve at a() before diving down the call stack.


void a()
{
    ...
    x = b();
    ...
}

int b()
{
    y = c();

    ...

    return y + d();
}

int d()
{
    ...
    z = e();
    ...
}
A: 

I would make my decision based on a couple of factors.

1) How clean is your code base. If your code base is relatively clean, it's easier to verify that your code is exception safe and you can catch exceptions at the top. But if the code is a hacked together mess, it will be easier to catch your exceptions low and rely on existing error handling.

2) How good is your team. Assuming you are not the only person coding, another issue to deal with is whether all your team members are up for writing exception safe code. If you have someone on your team who cannot break old habits, you should catch your exceptions low as such a programmer may introduce exception unsafe code over time.

R Samuel Klatchko
+3  A: 

Generally, I feel a system is best served by using a single error handling strategy throughout - every time you transition between the two, there are cracks that things can slip between. Most of the time I prefer exceptions in any language that reasonably supports them (ie, not in C - setjmp/longjmp be damned). Given your described design, with the majority of your system already using error codes, it seems to make sense to convert exceptions to error codes in e and pretend exceptions don't exist in your system.

On the other hand, are you sure only e can throw exceptions? It is fairly easy in C++ for exceptions to be thrown from a place you might not ordinarily expect - calling new, virtually any use of the standard library, and so on.

Note that exception safety, using techniques like RIAA, are also useful when you have code like

int* workspace[] = new int[500];
...
if(some_function() == ERROR)
  return SOME_FUNCTION_FAILED; // oops just leaked memory
...
delete[] workspace;

(or anything else that acquires resources)

RIAA techniques are thought of as being related to exceptions, but really it's just that they are utterly necessary there (since you have no way otherwise of doing it), where with error code handling techniques they are just very useful; while you can in theory handle all the resource releases yourself, realistically you can and will forget sometimes. Let The Compiler Do It(tm)

Do you really need try/catch around each and every operation? Why not wrap the entire function in a try/catch and return an error. For one thing, it makes it easier to distinguish between, say, a failure like reading a bad std::vector index and a memory allocation failure throwing std::bad_alloc.

The performance penalty for try/catch depends greatly on the ABI and compiler. I believe on a modern ABI (for instance the x86-64 Unix ABI) with a modern GCC the cost is non-zero but minimal, but on other compilers it can be noticeable. If you really want to know, you'll have to run experiments on your particular compiler/platform.

Jack Lloyd
Well put. Also, remember there are beasts like function try-blocks. Additional resources: C++ FAQ: http://www.parashift.com/c++-faq-lite/exceptions.html and Stroustrup on Exceptions: http://www.giref.ulaval.ca/~ctibirna/work/readings/stroustrup-pwex.html
dirkgently
+1, you need 'Exception Safety' as soon as you dive into multiple exit paths functions. Note that you can usually disable exception support in c++ if you write code based on returning error codes.
Matthieu M.
A: 

Regardless of what you decide here, I would encourage you to pound--or at least gently tap--the notion of eception-safety into the other developers' heads. In my experience, the process of writing exception-safe code has resulted in more cleanly-designed, transactional code.

As a benefit, that coding style works regardless of the presence of exceptions, whereas the reverse is not true.

Tom