14

longjmp関数が「アンワインド」する主要な C/C++ 実装はあり__attribute__((__cleanup__(...)))ますsetjmpか。私は特に、このプロパティを持つ POSIX 実装の存在 (または非存在) に関心がありますが、一般的な C/C++ も興味深いものです。

報奨金については、既に言及されている Windows とは対照的に、POSIX 準拠または少なくとも POSIX に似たシステムを探しています。

4

2 に答える 2

4

ここで達成しようとしている論理的な目標を理解しようとしています。

setjmp(3) マニュアルページには次のように記載されています。

setjmp() は、後で longjmp(3) が使用するためにスタック コンテキスト/環境を env に保存します。setjmp() を呼び出した関数が戻ると、スタック コンテキストは無効になります。

これは、setjmp() 呼び出しが行われたスタック コンテキストから戻ると、そこに longjmp で戻ることができなくなることを示しています。そうしないと、未定義の動作になります。

わかりました。したがって、有効な longjmp 呼び出しが行われた時点で、setjmp は現在のスタック コンテキストのどこかにある必要があるように思えます。したがって、スタックをアンワインドし、auto 変数などのすべてのデストラクタを呼び出す 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 : '/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
#include <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
  }
}

期待どおり、Ctrl-C の後に「sigint」が出力されます。この実装が POSIX 要件を満たしていないと主張する理由がわかりません。

于 2014-09-06T14:29:55.417 に答える