ルギア君の戯言

雑多な記事。

C++の禁じ手

自分は

新C++言語入門 シニア編〈上〉基本機能 (C++言語実用マスターシリーズ)

新C++言語入門 シニア編〈上〉基本機能 (C++言語実用マスターシリーズ)


新C++言語入門 シニア編〈下〉クラス機能 (C++言語実用マスターシリーズ)

新C++言語入門 シニア編〈下〉クラス機能 (C++言語実用マスターシリーズ)


を使っているわけだが、この本には書いていない禁じ手をいくつかメモ。
良く考えれば常識。

try ブロックの中に goto はできません。

例えば、こんなプログラム。

int main() {
  try {
    cant_goto_inside_try: ;
  } catch(...) {
    ....
  }
    goto cant_goto_inside_try;
}

まあ、これはコンパイルで弾かれるから何も問題はない。

main.cpp:50: error: jump to label ‘cant_goto_inside_try’
main.cpp:63: error:   ここから
main.cpp:43: error:   skips initialization of ‘int i’
main.cpp:45: error:   skips initialization of ‘dscript::ESystem err_dt’
main.cpp:63: error:   try ブロックに入ります

こんな感じで。ついでに i の初期化と err_dt の初期化をスルーしていますっていうエラーも出てますね。
try ブロックはそれきりしか実行しないという意味では try ブロックに何回も入るっていうのは確かにどうかと思うね。

C++ では setjmp、longjmp によってジャンプすることが困難です。

いえ、そんなに困難な話ではないです。


OKな例: (ヘッダは省略。)

int main() {
    jmp_buf jbuf;
    if(setjmp(jbuf) == 0) {
        fn(jbuf);
    } else {
        cout << "Returned from longjmp." << endl;
    }
}

void fn(jmp_buf jbuf) {
    cout << "function: fn" << endl;
    longjmp(jbuf, 1);
}

駄目な例: (ヘッダは省略。)

class jmp_dt {
  public:
    jmp_buf jbuf;
    int setjmp() { return ::setjmp(jbuf); }
    void longjmp(int n) { ::longjmp(jbuf, n); }
};

int main() {
    jmp_dt jbuf;
    for(int i = 0; i < 5; i++) {
        if(jbuf.setjmp() == 0) {
            jbuf.longjmp(1);
        } else {
            cout << "Returned from longjmp. (" << i << ")" << endl;
        }
    }
}

なぜ飛べないか。簡単に言えばクラスとの相性が良く無いから。
クラスの中に飛ぶとか言うのがムチャな話なんですよ。
だってすでにデストラクトされたオブジェクトの関数の中に戻ることなんでできませんもんね。
でもデストラクトされてなくてもできないものはできないんだっ(ぁ


↓libstdc++ (g++) での csetjmp の内容 (の一部)

//
// ISO C++ 14882: 20.4.6  C library
//

#pragma GCC system_header

#include <bits/c++config.h>
#include_next <setjmp.h>

#ifndef _GLIBCXX_CSETJMP
#define _GLIBCXX_CSETJMP 1

// Get rid of those macros defined in <setjmp.h> in lieu of real functions.
#undef longjmp

// Adhere to section 17.4.1.2 clause 5 of ISO 14882:1998
#ifndef setjmp
#define setjmp(env) setjmp (env)
#endif

_GLIBCXX_BEGIN_NAMESPACE(std)

  using ::jmp_buf;
  using ::longjmp;

_GLIBCXX_END_NAMESPACE

#endif