Существуют ли основные реализации C / C ++, где longjmp
функция «раскручивается», то есть где она взаимодействует с деструкторами для объектов автоматического хранения, __attribute__((__cleanup__(...)))
Обработчики отмены потоков POSIX и т. Д., А не просто восстановление контекста регистра, сохраненного setjmp
? Меня особенно интересует существование (или отсутствие) реализаций POSIX с этим свойством, но C / C ++ в целом также интересны.
Для награды я ищу POSIX-совместимую или, по крайней мере, POSIX-подобную систему, в отличие от Windows, которая уже упоминалась.
Interix (SUA) по умолчанию не вызывает деструкторы, но в режиме x86 для этого есть опция.
Принимая эту тестовую программу, сохраняется как test.cc
:
#include <stdio.h>
#include <setjmp.h>
struct A {
~A() { puts("~A"); }
};
jmp_buf buf;
void f() {
A a;
longjmp(buf, 1);
}
int main() {
if (setjmp (buf))
return 0;
f();
}
вот как ведет себя Interix. Для краткости я опустил требуемую правильную настройку PATH
,
$ cc -mx86 test.cc ./a.out $ cc -mx86 -X / EHa test.cc ./a.out cl: предупреждение командной строки D9025: переопределение '/ EHs' с помощью '/ EHa' ~ A $ cc -mamd64 test.cc ./a.out $ cc -mamd64 -X / EHa test.cc ./a.out cl: предупреждение командной строки D9025: переопределение '/ EHs' с помощью '/ EHa' $
Комментарии предполагают, что cc -X /EHa
не соответствует POSIX, например, потому что /EHa
будет ловить сигналы. Это не совсем верно:
$ cat test.cc #включают <signal.h> int main () { пытаться { поднимать (SIGFPE); } ловить (...) { // игнорируем } } $ cc -mx86 -X / EHa test.cc ./a.out cl: предупреждение командной строки D9025: переопределение '/ EHs' с помощью '/ EHa' Исключение с плавающей точкой (ядро сброшено)
Если я изменю raise(SIGFPE)
с делением на ноль, я действительно вижу, что обработчик исключений ловит его, но ни POSIX, ни C ++ не требуют какого-либо особого поведения для этого, так что это не влияет на соответствие. Это также не тот случай, когда все асинхронные сигналы отлавливаются: для этой программы:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sigint(int signal) {
puts("sigint");
exit(0);
}
int main() {
signal(SIGINT, sigint);
try {
for (;;) ;
} catch (...) {
// ignore
}
}
«sigint» печатается после Ctrl-C, как и ожидалось. Я не вижу причин утверждать, что эта реализация не соответствует требованиям POSIX.
Я пытаюсь понять логические цели, которые здесь пытаются достичь.
Страница руководства setjmp (3) гласит:
setjmp () сохраняет контекст / среду стека в env для последующего использования
longjmp (3). Контекст стека будет признан недействительным, если функция
который вызвал setjmp (), возвращает.
Это говорит о том, что если вы вернетесь из стекового контекста, где был сделан вызов setjmp (), вы больше не сможете вернуться к нему. В противном случае неопределенное поведение.
Итак, мне кажется, что в момент, когда выполняется правильный вызов longjmp, setjmp должен быть где-то в текущем контексте стека. Следовательно, longjmp, который разматывает стек и вызывает все деструкторы авто переменных и т. Д., Кажется логически эквивалентным выбрасыванию исключения и перехвату его в точке, в которой первоначально был сделан вызов setjmp ().
Чем создание и перехват исключения отличается от желаемой семантики setjmp / longjmp? Если, скажем, у вас была желаемая реализация setjmp / longjmp, то чем может отличаться ее замена обычным try / throw и перехватом сгенерированного исключения?
Единственные различия, которые я мог видеть, — это дополнительная внутренняя область действия, представленная блоком try / catch; в то время как setjmp действительно не открывает новую внутреннюю область видимости.
Таким образом, ответ здесь кажется очень простым: каждая совместимая реализация C ++ имеет эквивалент setjmp / longjmp, который имеет желаемую семантику. Это называется try / throw / catch.