2013-04-29 3 views
3

В jpeglib для реализации пользовательской обработки ошибок необходимо использовать setjmp/longjmp.Созданы ли объекты до разрушения setjmp?

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

Я этот пример (взяты из here и модифицированного немного):

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

ответ

7

Это может быть неопределенное поведение в зависимости от того, были ли или нет деструкторов бы назвать исключение, чтобы выполнить ту же самую передачу управления. В C++ 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) имеет более ограниченное поведение в настоящем стандарте. Ответная пара A 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() 

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


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

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

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

+0

Но в этом случае нет автоматических объектов, которые были бы уничтожены в результате исключения. – john

+0

Да, исключение не выбрасывается. –

+1

@ BЈовић, независимо от того, работает ли ваша конкретная среда или нет, это _never_ решение для UB. Иногда UB работает точно так, как вы ожидали, но он все еще UB, и может работать совершенно по-другому в чужой среде (или во время синей луны, или с разными параметрами компилятора и т. Д.). Для окончательной информации стандарт всегда является контрольным документом. – paxdiablo

Смежные вопросы