c++csetjmp

Is a longjmp from a C library into C++ code safe?


In C++, when working with a C library which uses longjmp for error handling. Is it safe to have the C code jump into C++ using setjmp. I know that jumping away from C++ code can lead to issues with destructors not being called but is the same true for jumping into C++ code.

Pseudo code of usage

if( setjmp( lib_get_jmpbuf() ) )
{
  throw "exception";
}

if( lib_question() )
{
    std::vector<int> stuff(5);

    lib_could_jump_way(stuff.data(), stuff.size());
}

If this is not safe, how do you interface with such C libraries safely?


Solution

  • As your sample is written, the library code could jump back into a different scope than the one from which it's called. And since the stuff vector is in that scope, I wouldn't expect it to be destroyed when the exception is thrown. (Stack unwinding for exception propagation is complex, so I could be wrong.)

    At a minimum, I'd hoist stuff above the setjmp().

    A better idea might be to create a C wrapper function for each library API that might throw. The wrapper should be responsible for the setjmp. Whether the library function returns or jumps, the wrapper should convert the result to a regular return. The C++ code then would call the wrapper function and either proceed or throw based on the return value.

    // wrapper.c (should be compiled as C, not C++)
    
    #include "wrapper.h"
    
    int lib_could_jump_wrapper(int *stuff, size_t size) {
        if (setjmp(lib_get_jmp_buf())) {
            return LIBRARY_JUMPED;
        }
        lib_could_jump_way(stuff, size);
        return LIBRARY_RETURNED;
    }
    
    // wrapper.h
    #define LIBRARY_RETURNED 0
    #define LIBRARY_JUMPED 1
    
    #ifdef __cplusplus__
    extern "C" {
    #endif
    
    int lib_could_jump_wrapper(int *stuff, size_t size);
    
    #ifdef __cplusplus__
    }
    #endif
    
    // Your C++ code:
    
    #include "wrapper.h"
    
    // ...
    
    if (lib_question()) {
        std::vector<int> stuff;
        if (lib_could_jump_wrapper(stuff.data(), stuff.size()) == LIBRARY_JUMPED) {
            throw "exception"
        }
        // ...
    }