C++ C/C++;longjmp在哪里展开的实现?

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

是否存在主要的C/C++实现,其中
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
  }
}