Chiharu の日記

絵描き C/C++ プログラマーの日記です。

setjmp/longjmp とデストラクタ 〜Visual C++ の場合

先ほどの続き。
Egtra さんの指摘で、x86 でも /EH オプションが有効の場合には setjmp/longjmp でデストラクタが起動することが分かりました。
状況を確認してみましょう。まず、下記のクラスを用意して ctor/dtor の呼び出し状況がコンソールから分かるようにします。

struct Test {
 Test(int val):num(val) {
  std::cout << "ctor (" << num << ")" << std::endl; }
 ~Test() { std::cout << "dtor (" << num << ")" << std::endl; }
 int num;
};

次いで下記のコードで setjmp/longjmp におけるデストラクタの動作状況を確認します。

jmp_buf gEnv;

void ok()
{
 Test aTest1(1);
 if (setjmp(gEnv) != 0) {
  return;
 }
 Test aTest2(2);
 longjmp(gEnv, 1);
}

int main()
{
 ok();
 return 0;
}

これらを x86 コンパイラと x64 コンパイラで下記のようにコンパイルします。

cl.exe test_jmp.cpp /EHsc

実行すると x86 では下記のように表示されます。

ctor (1)
ctor (2)
dtor (2)
dtor (1)

これは OK。
次いで、x64 では下記のように表示されます。

ctor (1)
ctor (2)
dtor (1)

あれれ?何かおかしい。ctor と dtor が対応づいていない。
さらにすんごく気になるケースがあったので追試。コード全体は下記のとおり。

#include <setjmp.h>
#include <iostream>

struct Test {
 Test(int val):num(val) { std::cout << "ctor (" << num << ")" << std::endl; }
 ~Test() { std::cout << "dtor (" << num << ")" << std::endl; }
 int num;
};

jmp_buf gEnv;

void ok()
{
 Test aTest1(1);
 if (setjmp(gEnv) != 0) {
  return;
 }
 Test aTest2(2);
 longjmp(gEnv, 1);
}

void ng()
{
 {
  Test aTest1(1);
  if (setjmp(gEnv) != 0) {
   return;
  }
 }
 Test aTest2(2);
 longjmp(gEnv, 1);
}

int main()
{
 std::cout << "case 1 started." << std::endl;
 ok();
 std::cout << "case 1 finished." << std::endl;
 
 std::cout << "case 2 started." << std::endl;
 ng();
 std::cout << "case 2 finished." << std::endl;
 
 return 0;
}

x86 の実行結果は下記のとおり…、なのですが最後にプログラムが落ちました。

case 1 started.
ctor (1)
ctor (2)
dtor (2)
dtor (1)
case 1 finished.
case 2 started.
ctor (1)
dtor (1)
ctor (2)
dtor (2)

x64 の実行結果は下記のとおりで、やはり ctor と dtor が対応づいていないようです。

case 1 started.
ctor (1)
ctor (2)
dtor (1)
case 1 finished.
case 2 started.
ctor (1)
dtor (1)
ctor (2)
dtor (1)
case 2 finished.

んー。やはりコンパイラのサポートがあるからといって、setjmp/longjmp と RAII の組み合わせは鬼門なのかな。