Question

In jpeglib, one has to use setjmp/longjmp to implement custom error handling.

There are lots of resources where it is said that setjmp/longjmp do not play well with c++ (for example answers in this question tell they do go along with RAII), but the answers to this question say that the destructor is called.

I have this example (taken from here and modified a bit):

#include <iostream>
#include <csetjmp>

std::jmp_buf jump_buffer;

struct A
{
    A(){std::cout<<"A()"<<std::endl;}
    ~A(){std::cout<<"~A()"<<std::endl;}
};

void a(int count) 
{
    std::cout << "a(" << count << ") called\n";
    std::longjmp(jump_buffer, count+1);  // setjump() will return count+1
}

int main()
{
    // is this object safely destroyed?
    A obj;

    int count = setjmp(jump_buffer);
    if (count != 9) {
        a(count);
    }
}

In this example, the destructor is called (as I expected), but is it the standard behaviour? Or is it compiler's extension, or simple UB?


The output:

A()
a(0) called
a(1) called
a(2) called
a(3) called
a(4) called
a(5) called
a(6) called
a(7) called
a(8) called
~A()
Was it helpful?

Solution

It can be undefined behaviour depending on whether or not destructors would be called were an exception to execute the same transfer of control. In C++03. From section 18.7 Other runtime support, paragraph 4:

The function signature longjmp(jmp_buf jbuf, int val) has more restricted behavior in this International Standard. 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.

There's similar language in c++11:

The function signature longjmp(jmp_buf jbuf, int val) has more restricted behavior in this International Standard. A setjmp/longjmp call pair has undefined behavior if replacing the setjmp and longjmp by catch and throw would invoke any non-trivial destructors for any automatic objects.

However, there appear to be no destructors that are called in transition for this particular piece of code, so I believe it's safe.


Now, if you were to change the code to move the creation to after the setjmp, that becomes undefined behaviour. In my setup (gcc 4.4.5 under Debian), the following code (with everything else identical to your question):

int main() {
    int count = setjmp (jump_buffer);
    A obj;
    if (count != 4) a (count);
}

results in the output:

A()
a(0) called
A()
a(1) called
A()
a(2) called
A()
a(3) called
A()
~A()

and you can see the destructor is not being called as part of the jump although, being undefined, it may be on some systems.


The bottom line is, you shouldn't be jumping from region A to region B where the equivalent throw/catch would properly destruct an object, because there's no guarantee the longjmp will call the destructor.

Actually, there are some who would say you shouldn't use setjmp/longjmp in C++ at all, and I tend to lean that way myself. I have a hard time seeing a need for that even in C.

I think I used it once in my entire career (and that's a long career), to implement co-operative threading in Turbo C under MS-DOS. I can't think of one other time I've ever used it. Not to say there aren't any uses, but they'd be pretty rare.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top