Реализации C / C ++, где longjmp раскручивается?

Существуют ли основные реализации C / C ++, где longjmp функция «раскручивается», то есть где она взаимодействует с деструкторами для объектов автоматического хранения, __attribute__((__cleanup__(...)))Обработчики отмены потоков POSIX и т. Д., А не просто восстановление контекста регистра, сохраненного setjmp? Меня особенно интересует существование (или отсутствие) реализаций POSIX с этим свойством, но C / C ++ в целом также интересны.

Для награды я ищу POSIX-совместимую или, по крайней мере, POSIX-подобную систему, в отличие от Windows, которая уже упоминалась.

14

Решение

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.

0

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

Я пытаюсь понять логические цели, которые здесь пытаются достичь.

Страница руководства 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.

4

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