В 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()
Это Можно быть неопределенным поведением в зависимости от того, будут ли вызваны деструкторы, были исключением для выполнения той же передачи управления. В С ++ 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. Я не могу думать о другой раз, когда я когда-либо использовал это. Не сказать там не любое использование, но они были бы довольно редки.
Других решений пока нет …