Hiding longjmps in C++ interface to C code

263 views Asked by At

What would be the right way to generate a C++ API for old C code that is extensively using longjmp with multiple jump targets for error management?

My idea was to write a function that sets jump targets for each used target, e.g.:

void catchJumps() {
    if (setjmp(target1)) throw Error1(); //Error1 and Error2 are some exception classes
    if (setjmp(target2)) throw Error2();
    //...
}

Then I'd call catchJumps in each C++-function (in each scope, to be more specific) that is using the C code:

int some_wrapper() {
    catchJumps();
    callCFunction()

    for (int i = 0; i < 1000; i++) {
        catchJumps();
        callOtherCFunction();
    }

    catchJumps();
    callOneMoreCFunction();
    callEvenOneMoreCFunction();
}

Is this a safe way to catch all the longjumps without destroying the stack? I know, that it's dangerous to longjmp into a different stack frame. Now my function catchJumps is in another stack frame than the calling some_wrapper. I'd hope (or can I even make it to) catchJumps can be inlined, so that the frame is the same, but I don't know.

The call in each scope (and after the loop above) should be necessary for all destructors of scoped objects to be called, right?

If this is not a valid method for 'converting' longjmps to assertions for the calling application, what else can we do?

2

There are 2 answers

2
vitaut On

You might have problems when using catchJumps with automatic objects that have destructors as explained in https://stackoverflow.com/a/1376099/471164 and citing 18.7/4 "Other runtime support":

If any automatic objects would be destroyed by a thrown exception transferring control to another (destination) point in the program, then a call to longjmp(jbuf, val) at the throw point that transfers control to the same (destination) point has undefined behavior.

I think a better approach is to create a wrapper for each C function that you use and that can do longjmp translating all these non-local gotos into exceptions. This will also make your code cleaner because you won't have catchJumps() all over the place but only in these wrapper functions.

2
Mark B On

Since you're stuck with such an API in the library, what about having catchJumps do the actual call by requiring a zero-parameter callable to be passed in and using a function pointer or boost/std::function?

template <typename CallMe>
void catchJumps(CallMe wrappee)
{
    if (setjmp(target1)) throw Error1(); //Error1 and Error2 are some exception classes
    if (setjmp(target2)) throw Error2();
    //...

    wrappee();
}

int some_wrapper()
{
    catchJumps(&callCFunction);

    for (int i = 0; i < 1000; i++)
    {
        catchJumps(&callOtherCFunction);
    }

    catchJumps(&callOneMoreCFunction);
    catchJumps(&callEvenOneMoreCFunction);
}