C++ での構造化例外の処理

C 構造化例外処理 (SEH) と C++ 例外処理との主な相違点は、C++ 例外処理モデルが複数の型を取り扱うのに対し、C 構造化例外処理モデルは 1 つの型 (特に、unsigned int) の例外を取り扱うことです。 つまり、C 例外は符号なし整数値で識別されますが、C++ 例外はデータ型によって識別されます。 構造化例外が C で発生すると、可能な各ハンドラーは、C 例外コンテキストをチェックするフィルターを実行して、例外を受け入れるか、他のハンドラーに渡すか、または無視するかを決定します。 C++ でスローされる例外は、どのような型でもかまいません。

2 番目の相違点は、例外がコントロールの標準フローに従属的に発生するので、C の構造化例外処理モデルが非同期と呼ばれることです。 C++ の例外処理機構は、完全な同期処理を提供します。つまり、例外は、スローされたときにのみ発生します。

/EHs または /EHsc コンパイラ オプションを使用する場合、構造化例外を処理する C++ 例外ハンドラーはありません。 これらの例外は、__except 構造化例外ハンドラーまたは __finally 構造化終了ハンドラーによってのみ処理されます。 詳細については、「構造化例外処理 (C/C++)」を参照してください。

/EHa コンパイラ オプションでは、C の例外が C++ プログラムで発生した場合、その例外は、関連のフィルターを持つ構造化例外ハンドラーまたは C++ catch ハンドラーのどちらか例外コンテキストに動的に近い方で処理できます。 たとえば、次のサンプル C++ プログラムでは、C++ try コンテキスト内で C 例外が発生します。

例 - C++ catch ブロックで C 例外をキャッチする

// exceptions_Exception_Handling_Differences.cpp
// compile with: /EHa
#include <iostream>

using namespace std;
void SEHFunc( void );

int main() {
   try {
      SEHFunc();
   }
   catch( ... ) {
      cout << "Caught a C exception."<< endl;
   }
}

void SEHFunc() {
   __try {
      int x, y = 0;
      x = 5 / y;
   }
   __finally {
      cout << "In finally." << endl;
   }
}
In finally.
Caught a C exception.

C 例外のラッパー クラス

上のような単純な例では、C 例外は省略記号 (...) catch ハンドラーによってのみキャッチできます。 ハンドラーに伝達される例外の種類または特性に関する情報はありません。 このメソッドは動作しますが、場合によっては、それぞれの C 例外が特定のクラスに関連付けられるように、2 つの例外処理モデル間の変換を定義した方がよい場合もあります。 この変換を行うために、特定のクラスの型を C 例外に起因すると見なすために利用したり派生させたりできる、C 例外の "wrapper" クラスを定義できます。 これにより、各 C 例外は、1 つのハンドラー内ですべて処理されるのではなく、特定の C++ catch ハンドラーによって個別に処理できます。

ラッパー クラスには、例外の値を決定して、C 例外モデルが提供する拡張例外コンテキスト情報にアクセスするメンバー関数で構成されるインターフェイスが含まれることがあります。 また、既定のコンストラクター、unsigned int 引数を受け入れるコンストラクター (基になる C の例外の表現を提供するため)、ビット単位のコピー コンストラクターを定義した方がよい場合もあります。 次は、C 例外のラッパー クラスの実装の例です。

// exceptions_Exception_Handling_Differences2.cpp
// compile with: /c
class SE_Exception {
private:
   SE_Exception() {}
   SE_Exception( SE_Exception& ) {}
   unsigned int nSE;
public:
   SE_Exception( unsigned int n ) : nSE( n ) {}
   ~SE_Exception() {}
   unsigned int getSeNumber() {
      return nSE;
   }
};

このクラスを使用するには、C 例外がスローされるたびに内部例外処理メカニズムによって呼び出されるカスタム C 例外変換関数をインストールします。 変換関数内で、適切な一致する C++ catch ハンドラーでキャッチできる、任意の型指定例外 (通常は SE_Exception 型、または SE_Exception から派生したクラス型) をスローできます。 変換関数は、代わりに制御を返すことができます。これは、例外を処理しなかったことを示します。 変換関数自体で C 例外が発生した場合は、terminate が呼び出されます。

カスタム変換関数を指定する場合は、変換関数の名前を単一の引数として指定して _set_se_translator 関数を呼び出します。 作成した変換関数は、try ブロックを持つスタックの関数呼び出しごとに 1 回呼び出されます。 既定の変換関数はありません。_set_se_translator を呼び出してそれを指定しない場合、C の例外は省略記号 catch ハンドラーでしかキャッチできません。

例 - カスタム変換関数を使用する

たとえば、次のコードでは、カスタム変換関数がインストールされた後に、SE_Exception クラスでラップされた C 例外が発生します。

// exceptions_Exception_Handling_Differences3.cpp
// compile with: /EHa
#include <stdio.h>
#include <eh.h>
#include <windows.h>

class SE_Exception {
private:
   SE_Exception() {}
   unsigned int nSE;
public:
   SE_Exception( SE_Exception& e) : nSE(e.nSE) {}
   SE_Exception(unsigned int n) : nSE(n) {}
   ~SE_Exception() {}
   unsigned int getSeNumber() { return nSE; }
};

void SEFunc() {
    __try {
        int x, y = 0;
        x = 5 / y;
    }
    __finally {
        printf_s( "In finally\n" );
    }
}

void trans_func( unsigned int u, _EXCEPTION_POINTERS* pExp ) {
    printf_s( "In trans_func.\n" );
    throw SE_Exception( u );
}

int main() {
    _set_se_translator( trans_func );
    try {
        SEFunc();
    }
    catch( SE_Exception e ) {
        printf_s( "Caught a __try exception with SE_Exception.\n" );
        printf_s( "nSE = 0x%x\n", e.getSeNumber() );
    }
}
In trans_func.
In finally
Caught a __try exception with SE_Exception.
nSE = 0xc0000094

関連項目

C (構造化) 例外と C++ 例外の混在