14

是否存在longjmp函数“展开”的主要 C/C++ 实现,即它与自动存储对象__attribute__((__cleanup__(...)))、POSIX 线程取消处理程序等的析构函数交互,而不仅仅是恢复由 保存的寄存器上下文setjmp?我对具有此属性的 POSIX 实现的存在(或不存在)特别感兴趣,但 C/C++ 通常也很有趣。

对于赏金,我正在寻找符合 POSIX 或至少类似 POSIX 的系统,而不是已经提到的 Windows。

4

2 回答 2

4

我试图理解这里试图实现的逻辑目标。

setjmp(3) 手册页指出:

setjmp() 将堆栈上下文/环境保存在 env 中,供 lo​​ngjmp(3) 以后使用。如果调用 setjmp() 的函数返回,堆栈上下文将失效。

这表示如果您从进行 setjmp() 调用的堆栈上下文返回,则不能再 longjmp 回到它。否则,未定义的行为。

好的,所以在我看来,在进行有效的 longjmp 调用时,setjmp 必须位于当前堆栈上下文中的某个位置。因此,展开堆栈并调用自动变量的所有析构函数等的 longjmp 在逻辑上似乎等同于抛出异常,并在最初进行 setjmp() 调用时捕获它。

抛出和捕获异常与您想要的 setjmp/longjmp 语义有何不同?比如说,如果你有你想要的 setjmp/longjmp 实现,那么用普通的 try/throw 替换它并捕获抛出的异常会有什么不同呢?

我能看到的唯一区别是 try/catch 块引入的额外内部范围;而 setjmp 并没有真正打开一个新的内部范围。

因此,这里的答案似乎很简单:每个兼容的 C++ 实现都有一个具有所需语义的 setjmp/longjmp 等效项。它被称为尝试/抛出/捕获。

于 2014-09-06T00:47:27.443 回答
0

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:用“/EHa”覆盖“/EHs”
〜一个
$ cc -mamd64 test.cc && ./a.out
$ cc -mamd64 -X /EHa test.cc && ./a.out
cl:命令行警告 D9025:用“/EHa”覆盖“/EHs”
$

评论表明cc -X /EHa不符合 POSIX,例如因为/EHa会捕获信号。这并不完全正确:

$猫测试.cc
#include <信号.h>
int main() {
  尝试 {
    提高(SIGFPE);
  } 抓住 (...) {
    // 忽视
  }
}
$ cc -mx86 -X /EHa test.cc && ./a.out
cl:命令行警告 D9025:用“/EHa”覆盖“/EHs”
浮点异常(核心转储)

如果我将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 要求。

于 2014-09-06T14:29:55.417 回答