●例外処理シミュレート2 [Except2]

(現在、内容がまとまっていません。ご了承ください)

1. ●最も上位の try 〜 catch ブロックの例

void  sample()
{
  try {
    // normal functions ...
  }
  catch ( Except2_Std*, msg ) {
    switch ( msg->code ) {
      case  BLex2_Err_Binary:    // 正式なエラー
      case  BLex2_Err_DefferentKakkoType:
      case  FileX_Err_FileNotFound:
        if ( MessageBox( NULL, msg->msg, "Knowledge Take! エラー",
             MB_OKCANCEL | MB_ICONERROR ) != IDOK )    // メッセージ表示
          Except2_Sys_exit(1);
      default:
        throw_again();
    }
  }
  finally {
    if ( step >= 1 )  finish( obj );
    if ( step >= 2 )  finish( obj );
    BigStack_end();
  } end_finally;
}


2. ●例外復帰エラー復帰

Except2 には、次の例外復帰機能があります。

2-1. エラー発生後に呼び出されるエラーハンドラ

ここでは、エラーと例外を分けています。どちらもプログラムが実行できない ケースのことですが、エラーの場合、内部的な例外復帰catch 節)処理は、 扱いません。例外復帰処理によってオブジェクトがエラーに対して「無視」の 処理を行ったこととし、その処理が終了して初めてエラー通知されます。 つまり、使用しているモジュールの内部については関与しません。

エラーハンドラは、その返り値によって、強制終了と復帰を選択できます。 エラーコード(タイプ)によって、エラーハンドラの振る舞いを 分けるとよいでしょう。

2-2. 例外ハンドラによる例外復帰(エラー無視)(復帰中のトレース)

例外が発生した瞬間に、例外ハンドラが呼ばれます。 そこからトレース実行して、例外復帰処理(catch節)をトレースします。


3. ●エラーメッセージ

エラーメッセージは、エラーログに記録する場合は、 デバッグ用としてフル・メッセージを記録します。 エラーメッセージ表示は、アプリユーザ向けとして printf 的に清書されたものを表示します。 ただし、モジュールサイズ最小化オプションによって その内容は変化します。

3-1. リリース時にも行うチェックのエラーメッセージ

Except2 モジュールは、try 文の外で例外が発生した場合、 強制終了の直前に強制的にエラーメッセージを表示します。 これを、例外に対するデフォルトとします。
try 文の中で例外が発生した場合、開発者が例外に対して エラー復帰処理を行うものとし、エラーメッセージの表示は 明示的に記述しない限り行いません。

ASSERT による事前チェック、事後チェックは、通常リリーズバージョンでは カットされ、パフォーマンスを向上させます。(ASSERT についての詳細は別記)
ただし、外部的にチェックできない場合、リリース時も内部的にチェックした方が いいでしょう。 その場合、1度だけエラーメッセージを表示して、エラー終了または エラー復帰(正常処理の無視)をします。 場合によってはエラー表示しない場合もあります。


4. ●ユーザ別例外処理(対応中)

例外は、モジュール内部で発生するので、例外の情報をすべて通知すると ユーザのデバッグに必要ないモジュール内部を見せることになります。 それでは都合が悪いことがあるため、本モジュールのユーザを次のように分けます。

一般ユーザ(以後、ユーザ)は、ライブラリ(本モジュールを使用した 一般的なモジュール)を使用するユーザとします。本モジュールを直接使わないため、 try ブロックを用いた例外処理を使いません。
ユーザは、基本的に返り値を参照して、エラーが発生したかどうかを確認します。 返り値でエラーを確認したら、エラー内容を確認したり、 ユーザ独自の異常終了手続きを踏んだりします。 エラー内容は、最もユーザに近いもののみ確認できます。 例外が起きるプログラムで返り値を無視すると、おそらく、 ほとんどの処理が内部で例外処理されるので、 ユーザ見えでは何もしないで処理が完了したように見えるでしょう。
すべてのエラーに対して処理する場合、エラーハンドラ(コールバック関数) を登録します。エラーハンドラは、返り値を返却する前に呼び出されます。 エラーハンドラで、ERRORS_EXIT を返すと、ライブラリの内部を含む 全てのエラーに対して(エラーハンドラ内で終了処理をした後)強制終了します。 ERRORS_IGNORE を返すと、全てのエラーを無視して続きを実行します。 Errors_isUserError 関数の返り値が true(ユーザエラー)のときに ERRORS_EXIT, そうでないときに ERRORS_IGNORE を返すようにすると、 ユーザが作成した ASSERT のみ強制終了するようになります。
エラーハンドラを登録しないと、ライブラリ内部の エラーメッセージをログに記録しません。 エラーハンドラを登録しないで ASSERT を用いた場合、 ユーザ独自の異常終了手続きを踏まずに強制終了します。 それでマズイ場合は、エラーハンドラを登録するか、 ASSERT を用いずにエラーコード返り値を判定して 異常終了手続きを記述します。

デベロッパ・ユーザ(以後ユーザ)は、ライブラリを提供するユーザとします。 ユーザは、本モジュールを用いて try ブロックを用いた例外処理を使います。 本モジュールを使用し、例外が発生した瞬間からトレースすることが出来ます。
ユーザは、一般ユーザと同じく返り値やエラーハンドラを用いる事も出来ますが、 エラーハンドラの内部で、全てのエラー内容を確認する関数を用いることが出来ます。 (ただし、スーパーデベロッパが作成したリリース版 のモジュールのエラー内容は確認できません)
try ブロック内部で発生した例外をトレースする場合、例外ハンドラを登録します。 例外ハンドラは、初めて例外が throw された瞬間に呼び出され、 以後、それぞれの例外が発生する度に呼び出されます。 エラーハンドラが呼び出される直前でも呼び出されます。

スーパーデベロッパ・ユーザは、デベロッパが使用するライブラリを提供する ユーザとします。本モジュールで使える機能はデベロッパと同じですが、 リリース版でコンパイルした オブジェクトモジュールを提供し、 デベロッパにライブラリ内部を見せないようにすることが出来ます。


5. ●try 〜 catch ブロックのマクロの展開内容

標準関数 setjmp 〜 longjmp を使用しています。
詳細は、実際のマクロの内容を確認してください。

if ( setjmp(a) ) {                   // try
  // normal functions ...
}
else if ( type && ! throw_again ) {  // catch
  // resume functions ...
  throw( longjmp(a) );               // throw again
}
{                                    // finally or end_catch(1)
  // final functions ...
}
if ( throw_again )  longjmp(up)      // end_finally or end_catch(2)
if ( no catch )     throw( longjmp(up) )

再スローは、catch 内で行った場合と、catch されなかった場合とでは、 経路が異なります。
例外オブジェクトは end_finally の内部で、 または別の例外オブジェクトを再スローしたときに消滅します。

finally 節は次の要求を満たしています。


written by Masanori Toda from Dec.21.1998