views:

362

answers:

6

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.

A: 

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

1800 INFORMATION
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.
anon
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
1800 INFORMATION
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.
anon
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.
1800 INFORMATION
So what is your solution? I'm afraid a crash will not do - that gives you even less information than a log entry.
anon
A crash gives you a memory dump, you can then debug it with a debugger and fix the issue.
1800 INFORMATION
+1  A: 

What about:

try{
    //Your code here
} catch(std::exception e)
{
   return translateExceptionToErrorCode(e);
} catch(...)
{
   return UNKNOWN_EXCEPTION_THROWN;
}
Visage
Normally you'd catch an exception by reference, to avoid unnecessary copying
James Hopkin
Not to mention slicing.
anon
Very good points. Treat my code as pseudo ;)
Visage
+14  A: 

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();
}
Jem
+1: Good idea :-)
James Hopkin
Worked perfectly. Thanks!
Laserallan
Jem
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).
Martin York
A: 

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

Carl Seleborg
Translating the exceptions to error codes seems to be exactly what he is doing!
anon
+1  A: 

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;
}
Nic Strong
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 .. :/
yves Baumes
+2  A: 

There already is a good answer. But just FYI, its called 'exception-dispatcher' idiom, see C++ FAQ 17.9. IMHO 17.10 is also an important read.

Abhay
Thanks, this was great reading.
Laserallan