Разрушены ли объекты, созданные до setjmp?

В jpeglib, нужно использовать setjmp / longjmp для реализации пользовательской обработки ошибок.

Есть много ресурсов, где говорится, что setjmp / longjmp не очень хорошо работает с c ++ (например, ответы в этот вопрос сказать, что они идут вместе с RAII), но ответы на этот вопрос скажем что деструктор называется.

У меня есть этот пример (взят из Вот и немного доработал):

#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);
}
}

В этом примере деструктор вызывается (как я и ожидал), но это стандартное поведение? Или это расширение компилятора, или просто UB?


Выход:

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()

3

Решение

Это Можно быть неопределенным поведением в зависимости от того, будут ли вызваны деструкторы, были исключением для выполнения той же передачи управления. В С ++ 03. Из раздела 18.7 Other runtime support, paragraph 4:

Подпись функции longjmp(jmp_buf jbuf, int val) имеет более ограниченное поведение в этом международном стандарте. Если какие-либо автоматические объекты будут уничтожены вызванным исключением, передающим управление в другую (целевую) точку программы, вызовом longjmp(jbuf, val) в точке выброса, которая передает управление в ту же (целевую) точку, поведение не определено.

Есть похожий язык в C ++ 11:

Подпись функции longjmp(jmp_buf jbuf, int val) имеет более ограниченное поведение в этом международном стандарте. setjmp/longjmp Пара вызовов имеет неопределенное поведение при замене setjmp а также longjmp от catch а также throw будет вызывать любые нетривиальные деструкторы для любых автоматических объектов.

Тем не менее, как представляется, не существует деструкторов, которые вызываются в переходе для этого конкретный кусок кода, так что я считаю, что это безопасно.


Теперь, если вам нужно изменить код, чтобы переместить создание в после setjmp, это становится неопределенным поведением. В моей настройке (gcc 4.4.5 под Debian), следующий код (все остальное идентично вашему вопросу):

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

результаты на выходе:

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

и вы можете увидеть деструктор не вызывается как часть прыжка, хотя, будучи неопределенным, это может происходить в некоторых системах.


Суть в том, что вы не должны прыгать из области A в область B, где эквивалент throw/catch будет правильно разрушить объект, потому что нет никаких гарантий longjmp вызовет деструктор.

На самом деле, есть те, кто сказал бы, что вы не должны использовать setjmp/longjmp в C ++ совсем, и я склонен так склоняться. Мне трудно это увидеть даже в C.

Я думаю, что использовал это один раз во всей моей карьере (и это долго карьера), чтобы реализовать совместную многопоточность в Turbo C под MS-DOS. Я не могу думать о другой раз, когда я когда-либо использовал это. Не сказать там не любое использование, но они были бы довольно редки.

7

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]