Will C++ Exceptions Safely Propagate Through C Code

Will C++ exceptions safely propagate through C code?

My guess is that this is compiler dependent. However, throwing an exception in the callback would be a very bad idea. Either it will flat-out not work, or the C code in the SQLite library will be unable to handle it. Consider if this is some code in SQLite:

{
char * p = malloc( 1000 );
...
call_the_callback(); // might throw an exception
...
free( p );
}

If the exception "works", the C code has no possible way of catching it, and p will never be freed. The same goes for any other resources the library may have allocated, of course.

A mechanism to ensure exception propagation when mixing C and C++ code

Edit : You can use fungo, a better implementation of the idea I described below. From its author :

fungo is a C++ library that is designed for those of us stuck using older C++ implementations that do not yet support std::exception_ptr.

In other words, fungo allows you to make a fair attempt at storing and later re-throwing exceptions caught by a catch(...) block. This is useful for propagating exceptions across thread-joins or over C/C++ boundary interfaces.

I'll mark this as an answer.


As I mentioned in my question, I cannot use C++0x/11 features for now (using new features isn't planned for now), and I will present here what I have done so far :

Exceptions have a lifetime that spans across the try-catcher block. In order to save an exception, one must create a copy on the heap. We get rid of the copy when re-throwing the exception. I wrote an exception holder interface :

class ExceptionHolderInterface
{
public :
ExceptionHolderInterface(void) ;
virtual ~ExceptionHolderInterface(void) ;

/* For holding an exception. To be called inside a catch block.*/
virtual void Hold(void) = 0 ;

/* For releasing an exception. To be called inside a try block.*/
virtual void Release(void) = 0 ;
private :
} ;

This is a type independent class. The exception type is introduced using templates :

template<typename ExceptionType>
class ExceptionHolder : public ExceptionHolderInterface
{
public :
ExceptionHolder(void) ;
virtual ~ExceptionHolder(void) ;

virtual void Hold(void)
{
try
{
throw ;
}
catch(const ExceptionType & e)
{
exception.reset(new ExceptionType(e)) ;
}
}

virtual void Release(void)
{
if(exception.get())
{
throw ExceptionType(*exception.get()) ;
}
}
private :
std::auto_ptr<ExceptionType> exception ;

// declare the copy-constructor and the assignment operator here to make the class non-copyable
} ;

I removed a bunch of tests/optimizations/verifications, I kept the main idea. So far we have an exception holder for one type, so we can build an exception store that can hold many types at a time.

class ExceptionStore
{
public :
ExceptionStore(void) ;
~ExceptionStore(void)
{
for(Iterator holder = exception_holders.begin() ; holder != exception_holders.end() ; ++holder)
{
delete (*holder) ;
}
}

// Add an exception type to handle
template<typename ExceptionType>
void AddExceptionHolder(void)
{
exception_holders.push_back(new ExceptionHolder<ExceptionType>()) ;
}

// Try to hold an exception using available holders. Use this inside a catch block.
void Hold(void)
{
Iterator holder = exception_holders.begin() :
while(holder != exception_holders.end())
{
try
{
(*holder)->Hold() ;
break ;
}
catch(...)
{
++holder ;
}
}
}

// Try to release any hold exception. Call this inside a try-block.
void Release(void)
{
Iterator holder = exception_holders.begin() :
while(holder != exception_holders.end())
{
(*holder++)->Release() ;
}
}

private :
std::list<ExceptionHolderInterface *> exception_holders ;
typedef std::list<ExceptionHolderInterface *>::iterator Iterator ;

// Declare the copy-constructor and the assignment operator here to make the class non-copyable
} ;

I can use the exception store as shown below :

// I made a global ExceptionStore just to keep the example simple.
ExceptionStore exception_store ;

void callable_from_c_code(void)
{
// Normally, we should retrieve the exception store in some manner.

try
{
// Do processing here. Exceptions may be thrown.
}
catch(...)
{
// Something wrong happened. Let's store the error for later.
exception_store.Hold() ;
}

// Exceptions do not propagate to C code.
}

int main(int, char * [])
{
// First, set the exception types we want to handle. The handling is done in
// the same order as below.
exception_store.AddExceptionHolder<std::runtime_error>() ;
exception_store.AddExceptionHolder<std::logic_error>() ;
exception_store.AddExceptionHolder<MyFancyException>() ;

// Somehow invoke some C code that uses `callable_from_c_code`
use_some_c_library_with_callback(&callable_from_c_code) ;

// Handle any caught exception
try
{
exception_holder.Release() ;
}
catch(std::exception &)
{
// Something gone wrong ...
}
catch(MyFancyException &)
{
// Nothing fancy despite the name. We have problems here ...
}
}

This is very basic, and there might be some unexpected scenarii that are not handled by this example. If an exception with a type not declared using AddExceptionHolder is throw, you have two possibilities :

  • A holder for a base type is added to the store, so the exception will be caught and sliced, keeping only a copy of the base type.
  • No holder fits the exception, and it is simply lost. Nothing leaks to the C land.

For now, I prefer using this solution to the more tested/used/verified boost::enable_current_exception because I can't afford refactoring the whole C++ code to surround all throw sites with boost::enable_current_exception(...).

Anyway, the std::exception_ptr seems to be the perfect solution, and I will replace the above code once I can move to the new C++ standard.

Can a C program handle C++ exceptions?

C doesn't have exceptions, therefore in general you should catch all exception and return an error code and/or provide a function that returns the information about the last error.

Behavior of c++ exceptions escaping into c program

From what I see about C++ exceptions, in this example which I took from MSDN, GCC seems to include the following assembly in the catch statement:

    call    __cxa_end_catch
jmp .L37
movq %rax, %rbx
call __cxa_end_catch
movq %rbx, %rax
movq %rax, %rdi
call _Unwind_Resume

Which makes use of what I can only assume are C++ library calls to functions that deal with exceptions (e.g. _Unwind_resume). So if the C code links against your library it will have to provide these symbols/functions which means that the code is going to be entering the C++ library to deal with the exceptions.

However, I don't yet know what the C++ library requires in order to do its job. I would expect it to be self contained but I'm not certain of it.

Edit: The answer to this question likely lies in the answers to the following two existing questions (and their interpretation):

  1. How is the C++ exception handling runtime implemented?
  2. How are exceptions implemented under the hood?
  3. How do exceptions work (behind the scenes) in c++

Edit 2: From this answer, it seems that since __cxa_throw uses a table for keeping track of available handlers. I would assume that when the table is exhausted, which in our case occurs when we enter C code, the function would call std::terminate. Hence, the C++ runtime (against which you must have linked) should take care of this for you without you needing to put up a catch all clause.

Since I'm still uncertain I will write up a test of this theory and update the answer with the results.

Throwing C++ exception through C function call

This is platform-specific and compiler-specific question.

For example, on Linux/GCC, you have to compile C code with -fexceptions option, then unwind tables would be build and exception will go throw C code.

From https://gcc.gnu.org/onlinedocs/gcc-7.3.0/gcc/Code-Gen-Options.html#index-fexceptions

-fexceptions

Enable exception handling. Generates extra code needed to propagate
exceptions. For some targets, this implies GCC generates frame unwind
information for all functions, which can produce significant data size
overhead, although it does not affect execution. If you do not specify
this option, GCC enables it by default for languages like C++ that
normally require exception handling, and disables it for languages
like C that do not normally require it. However, you may need to
enable this option when compiling C code that needs to interoperate
properly with exception handlers written in C++. You may also wish to
disable this option if you are compiling older C++ programs that don’t
use exception handling.

I'm less familiar with Visual C++/Windows development, but I believe exceptions handling will use common mechanisms if you compile your C++ and C code with /EHa option (allow mix of structured and C++ exceptions)

What happens if a C++ shared lib throws an exeption in a C project

If a C++ exception bubbles up into C code then you'll get undefined behaviour. This is because the stack frames in each language for foreign to each other. The C++ runtime will attempt to unwind the stack frame into the C code and will assume that the stack frame and runtime is set up for C++ when it's actually set up for C.

If you're lucky your app will crash and you'll soon know about the problem. If you're unlucky it'll carry on for a while and you'll get some hard to find bug!

*When* does a C++ exception blow up C code

The problem with this question, is it asks what is the defined behavior for an undefined result.

The C standard does not describe the behavior when an exception is thrown through a C function, because it shouldn't happen.

The C++ standard does not explain the mechanism for an uncaught exception dropping through a C function because it shouldn't happen.

A practical example

Windows Visual C++ uses the fs: segment register for thread local storage, and a particular slot in the thread-local data segment to create a linked list of catch frames.

When a C++ exception is thrown, the linked list is inspected for destructors of stack objects, and a suitable catch frame.

The C compiler may not be aware of the C++ usage of these resources, and be able to re-use the slots for a different purpose. If the slots are used for an incompatible function, then a crash will happen.

A particular platform and compiler may support this, but you would be at the whim of the platform for such.

gcc is a C compiler, so does not throw exceptions. It can create code which is exception aware as it supports -fexception.

gnu compiler : using exceptions suggests compiling the C code with -fexceptions and says

In particular, unwinding into a frame with no exception handling data will cause a runtime abort.

Although the context of that statement does not describe whether that is due to being debugged clause, or due to the exception mechanism identifying an illegal state and causing an abort.

The actual implementation would be hardware and OS specific, which is not specified in the question, and for a specific answer, these should be specified.

  • C++ does not have a compatibility ABI. Calling through C++ objects compiled with different compilers does not give a guarantee of working.
  • exceptions thrown between different modules is not recommended, as even the implementation on a compiler has produced code which is not intra-version compatible.
  • Assuming that you have control over both the C and C++ code, ensure the C is safe by enabling the exceptions and possibly compiling the C as C++ (with an extern "C" wrapper, ensuring it is a fully C++ compilation unit.


Related Topics



Leave a reply



Submit