19

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.

6 Answers 6

29

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();
}
Sign up to request clarification or add additional context in comments.

5 Comments

In real code do not forget to catch exceptions by reference: catch( std::bad_alloc & )
Will HandleException see exception thrown outside?
You need to explain the re-throw. This will be a very new concept to a lot of developers (especially beginners).
@LokiAstari This could become a great post, I'm not aware of this re-throw mechanism as well so I'm highly interested in it.
Instead of int, try returning struct fake_almost_anything { template<class T> operator T() { return *(T*)nullptr; } from HandleException. And in C++11 mark it as [[noreturn]].
5

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

2 Comments

The link is once again out of date, the C++ FAQ is now on www.isocpp.org.
1

What about:

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

1 Comment

Normally you'd catch an exception by reference, to avoid unnecessary copying
1

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;
}

1 Comment

that's what I've though first too, (and actually what I've used in some of my projects). But Jem's anwser is cleaner from my point of view. While it doesn't disturb me , sometimes templates use disturbs other programmers .. :/
0

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".

6 Comments

In this case I think the use of catch(...) is the correct thing to do. In C++ it is difficcult, if not impossible, to predict all exceptions that can be thrown, but he must prevent them from propagating to his C code.
If you do this, then plan on entering a hell of bad bug reports with little to no information about what caused the problem and no way of getting any more
So you would rather the program crashed because some obscure library you depend on at two removes decides it is going to throw MyWeirdError? I think I'd rather see "Unidentified problem with operation XXX" in the log.
Would you rather crash, or maybe corrupt memory, or leave a deadlock waiting to happen, or forget to close a file, or unlock a mutex? You have maybe avoided the perception of instability by losing the only information you will ever get about what had caused it.
So what is your solution? I'm afraid a crash will not do - that gives you even less information than a log entry.
|
0

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

1 Comment

Translating the exceptions to error codes seems to be exactly what he is doing!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.