Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hiding longjmps in C++ interface to C code

Tags:

c++

c

api

setjmp

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?

like image 478
urzeit Avatar asked Dec 19 '13 16:12

urzeit


2 Answers

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.

like image 58
vitaut Avatar answered Sep 19 '22 21:09

vitaut


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);
}
like image 23
Mark B Avatar answered Sep 22 '22 21:09

Mark B