Laserallan
Laserallan

Reputation: 11342

Code reuse in exception handling

I'm developing a C api for some functionality written in C++ and I want to make sure that no exceptions are propagated out of any of the exported C functions.

The simple way to do it is making sure each exported function is contained in a:

try {
   // Do the actual code
} catch (...) {
   return ERROR_UNHANDLED_EXCEPTION;
}

Let's say I know one exception that is often missed inside the C++ code is std::bad_alloc and I want to treat it specially I'd write something like this instead:

try {
   // Run the actual code
} catch (std::bad_alloc& e) {
   return ERROR_BAD_ALLOC;
} catch (...) {
   return ERROR_UNHANDLED_EXCEPTION;
}

Is it possible to decompose this in some clever way so that I can globally treat some errors differently without adding a new catch statement for the exception handler around every exported function?

I'm aware of that this is possible to solve using the preprocessor, but before going down that road, I'd make sure there is no other way to do it.

Upvotes: 19

Views: 2641

Answers (6)

Abhay
Abhay

Reputation: 7190

There already is a good answer. But just FYI, its called 'exception-dispatcher' idiom, see C++ FAQ.

Upvotes: 5

Nic Strong
Nic Strong

Reputation: 6592

Jem answer is a little more simpler than this solution. But it is possible to substitute the use of a preprocessor macro with the use of templates. Something like this (more refinements you could made):

template <class T, void (T::*FUNC)()>
class CatchWrapper
{
public:

    static void WrapCall(T* instance)
    {
        try
        {
            (instance->*FUNC)();
        }
        catch (std::bad_alloc&)
        {
            // Do Something 1
        }
        catch (std::exception& e)
        {
            // Do Something 2
        }
        catch (...)
        {
            // Do Something 3
        }
    }
};


class Foo
{
public:
    void SomeCall()
    {
        std::cout << "Do Something" << std::endl;
    }
};


int main(int argc, char* argv[])
{
    Foo i;
    CatchWrapper<Foo, &Foo::SomeCall>::WrapCall(&i);
    return 0;
}

Upvotes: 1

Jem
Jem

Reputation: 2275

You can use only one handler function for all possible exceptions, and call it from each or your API implementation functions, as below:

int HandleException()
{
    try 
    {
        throw;
    }

    // TODO: add more types of exceptions

    catch( std::bad_alloc & ) 
    {
       return ERROR_BAD_ALLOC;
    }
    catch( ... )
    {
        return ERROR_UNHANDLED_EXCEPTION;
    }
}

And in each exported function:

try
{
    ...
}
catch( ... )
{
    return HandleException();
}

Upvotes: 29

Carl Seleborg
Carl Seleborg

Reputation: 13305

It would be a shame to loose error information at the language boundary. You really should try to translate all exceptions into an error code usable from C.

How you do it really depends on what your exception classes look like. If you control your exception class hierarchy, you can ensure that each class provides a translation using a virtual method. If not, you may still find it practical to use a translator function and test the types of the 'std::exception'-derived exception it receives to translate it into an error code, much like Jem suggested (remember: thrown exceptions will hurt performance anyway, so don't worry about the translation being slow).

Upvotes: 0

pauljwilliams
pauljwilliams

Reputation: 19225

What about:

try{
    //Your code here
} catch(std::exception e)
{
   return translateExceptionToErrorCode(e);
} catch(...)
{
   return UNKNOWN_EXCEPTION_THROWN;
}

Upvotes: 1

1800 INFORMATION
1800 INFORMATION

Reputation: 135463

Do not ever use catch(...), unless you plan on more or less immediately re-throwing. You will certainly lost any error information you might have had to help you figure out the cause of the error.

I like your second scheme a little better - catch a known set of exceptions, ideally because they are the only ones your code will throw, and let the rest through - allowing the app to crash is possibly the best thing to do since you have invoked unknown behaviour it is best to "crash responsibly".

Upvotes: 0

Related Questions