C++ C/C++;longjmp在哪里展开的实现?
是否存在主要的C/C++实现,其中C++ C/C++;longjmp在哪里展开的实现?,c++,c,exception-handling,stack-unwinding,setjmp,C++,C,Exception Handling,Stack Unwinding,Setjmp,是否存在主要的C/C++实现,其中longjmp函数“展开”,即它与自动存储对象的析构函数、\uuuu属性((\uu cleanup\uuuu(…))、POSIX线程取消处理程序等交互,而不仅仅是恢复setjmp保存的寄存器上下文?我对具有此属性的POSIX实现的存在性(或不存在性)特别感兴趣,但C/C++通常也很有趣 作为奖励,我正在寻找一个符合POSIX标准或至少类似POSIX的系统,而不是已经提到的Windows。我试图理解这里要实现的逻辑目标 setjmp(3)手册页面说明: setjm
longjmp
函数“展开”,即它与自动存储对象的析构函数、\uuuu属性((\uu cleanup\uuuu(…))
、POSIX线程取消处理程序等交互,而不仅仅是恢复setjmp
保存的寄存器上下文?我对具有此属性的POSIX实现的存在性(或不存在性)特别感兴趣,但C/C++通常也很有趣
作为奖励,我正在寻找一个符合POSIX标准或至少类似POSIX的系统,而不是已经提到的Windows。我试图理解这里要实现的逻辑目标 setjmp(3)手册页面说明: setjmp()将堆栈上下文/环境保存在env中,以供以后使用 longjmp(3)。如果函数 它调用setjmp()返回 这意味着,如果您从进行setjmp()调用的堆栈上下文返回,就不能再将jmp返回到它。否则,将出现未定义的行为 好的,在我看来,在进行有效的longjmp调用时,setjmp必须位于当前堆栈上下文中的某个位置。因此,展开堆栈并调用自动变量的所有析构函数等的longjmp在逻辑上似乎等同于抛出异常,并在最初进行setjmp()调用时捕获异常 抛出和捕获异常与所需的setjmp/longjmp语义有何不同?如果您希望实现setjmp/longjmp,那么用普通的try/throw替换它并捕获抛出的异常会有什么不同呢 我能看到的唯一区别是try/catch块引入的额外内部作用域;而setjmp并没有真正打开一个新的内部作用域 <>所以,这里的答案看起来很简单:每个兼容的C++实现都有一个具有所需语义的SETJMP/LangJMP等价物。它被称为try/throw/catch。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();
}
评论表明,cc-X/EHa
不符合POSIX,例如,因为/EHa
会捕捉信号。这并不完全正确:
$ cat test.cc
#include <signal.h>
int main() {
try {
raise(SIGFPE);
} catch (...) {
// ignore
}
}
$ cc -mx86 -X /EHa test.cc && ./a.out
cl : Command line warning D9025 : overriding '/EHs' with '/EHa'
Floating point exception (core dumped)
“sigint”按预期在Ctrl-C之后打印。我看不出有任何理由声称此实现无法满足POSIX要求。我严重怀疑。是的,你最喜欢的,MSVC++实现了它。@Christophe。但是说如果你用
/EH
编译它会调用析构函数。我建议你抛出一个异常,在你想要停止解绕的地方捕获它。说得清楚一点,你不在乎这个行为是否合法,你只在乎这样的实现是否存在?因为,C++11说在代码上使用setjmp
/longjmp
,其中展开会调用非平凡的析构函数,从而导致未定义的行为。我的兴趣不在于是否有类似于longjmp
的东西展开,而是longjmp
是否展开。请参阅comments.No中迄今为止的讨论。POSIX定义的类似于解除的最接近的东西是线程取消,当您使用longjmp
绕过清理上下文时,会发生什么,这是显式的未定义的。因此,单个实现可以不定义它,或者尝试为随后发生的事情定义行为。当然,POSIX对于C++异常的交互没有什么可说的,因为从POSIX的角度来看,C++甚至不存在。奖牌总是有两面的。系统必须支持POSIX一致性,并且程序必须使用它。要不是赏金,R。。正在搜索兼容POSIX的实现(第1个属性),该实现在展开期间调用析构函数(第2个属性)。当然,Interix支持这两个属性,但只有当您以不支持第一个属性的方式编译程序时,才支持第二个属性。所以我认为这是有问题的。顺便说一句,微软甚至警告他们的用户在便携式程序中使用SEH和EHa。我没有链接,但很容易证明。如果使用/EHa
编译不会中断POSIX信号,则以下程序将具有退出代码!=0,因为出现致命错误,例如CentOS上的错误:。这是因为POSIX定义退出代码设置为一个值!=如果信号未经处理,则为0。使用/EHa
编译它时,让它返回0,因为每个异常都会被捕获(div0异常也是如此)。@StefanWeiser:该程序调用未定义的行为,因此POSIX对它的功能没有任何说明。如果将其更改为raise(SIGFPE)
,是否会发生同样的情况?@StefanWeiser不仅是程序的行为未定义为R。。指出,如果定义了它,它甚至不能证明你的观点。POSIX允许<代码> catch(…)>代码>对提升的信号有影响,因为它根本不说任何关于C++的任何事情。(我还没有测试过任何东西。)@hvd,@R..:我想,我们在兼容性方面没有相同的说法。如果一个程序在每个POSIX兼容系统上都会产生相同的行为,那么它就是POSIX兼容的IMO。您可以将SIGFPE与MS抛出并因此捕获的任何其他信号交换。顺便说一句,只要该行为不是由raise
或kill
引发的,则该行为是未定义的。所以我可以用raise更新程序。。。同样的结果。。。
$ cat test.cc
#include <signal.h>
int main() {
try {
raise(SIGFPE);
} catch (...) {
// ignore
}
}
$ cc -mx86 -X /EHa test.cc && ./a.out
cl : Command line warning D9025 : overriding '/EHs' with '/EHa'
Floating point exception (core dumped)
#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
}
}