プログラムの実行中にエラー等の「例外」が発生した場合、本来の処理を継続できなく なります。この時、制御は「例外処理」に移行します。「例外」とは、本来の処理を継続 できなくなる事態のことです。また、それに対応する処理が「例外処理」です。エラーは、 例外の代表的なものですが、例外は、必ずしもエラーだけではありません。しかし、例外 とエラーを同義で使う場合もあります。 本言語システムが管理するエラーに対応する例外処理は、以下の順で行なわれます。 (1)エラー情報の設定 (2)エラー代替 (3)エラーワープ つまり、プログラム実行中にエラーが発生すると、まず、そのエラー情報が設定されます。 次に、そのエラーの代替手段が確認されます。それが設定されていれば、そのエラーは、 その手段で代替されて、制御は、本来の処理に戻ります。代替手段が設定されていないと、 最後に、エラーワープが確認されます。それが設定されていれば、そのワープ先に制御が 移行します。設定されていないと、そのスレッドの実行は異常終了します。エラー代替と エラーワープは、最初は未設定なので、適時設定する必要があります。 ユーザー独自の例外処理は、例えば、ワープによって行なうことができます。その際、 スレッドローカルスコープ内等に、その例外情報を設定して渡すことができます。 ●エラー情報 プログラム実行中にエラーが発生した時に、そのエラー情報が、現実行中のスレッドの スレッドローカルスコープ内に自動的に設定されます。各エラー情報が格納される変数名 とその内容を、次に示します。
err_code エラー識別番号(整数値) err_info エラー付加情報(文字列) err_func エラーが発生した関数の名前(文字列) err_state エラーワープ状態フラグ(整数値)
この各エラー情報の変数にアクセスするには、各変数名の前にスレッドローカルスコープ を規定する演算子 $ を付加する必要があります。 エラー識別番号とエラー付加情報に対応するエラーメッセージは、::ErrorMessage と いうシステム組み込み関数を、次の形式でコールすることによって、取得できます。
::ErrorMessage( エラー識別番号, エラー付加情報 )
例えば、上記の $err_code, $err_info に対応するエラーメッセージをプリントするには、 次のようにします。
print ::ErrorMessage( $err_code, $err_info );
エラーワープ状態フラグ $err_state については、「エラーワープ」の節で説明します。 ●エラー代替 本言語では、プログラム実行中に発生したエラーを代替する手段が提供されています。 例えば、存在しない変数の値を読み出そうとした場合、通常はエラーになりますが、その 「代替値」が設定されていれば、その値が対象の変数の値の代わりに使われて、その処理 が継続されます。また、未定義の関数をコールしようとした場合も、エラーになりますが、 その「代替関数」が設定されていれば、その関数が対象の関数の代わりにコールされて、 その処理が継続されます。 (1)代替値 不在/不正の箱の中身を読み出そうとした時には、例外が発生します。それに対処する ために「代替値」が設定できるようになっています。代替値を設定しておけば、この例外 が発生した時、その不在/不正の箱の値の代わりに、代替値が使用されて、プログラムの 実行が継続します。代替値は、DefValue という名前の箱に設定しておきます。例えば、
DefValue = 0; print A;
の実行では、プリント対象の変数 A が不在なので、この値は、DefValue の値で代替され て、0 がプリントされます。この例では、DefValue は、現実行関数のローカルスコープ 内に設定しましたが、他のスコープに設定しておくこともできます。また、DefValue に は、整数以外のデータ型も設定できます。特に、null を設定する場合はよくあります。 DefValue 箱は、以下の順に各スコープで検索されて、最初に見つかった箱が採用され ます。 (1) 関数ローカルスコープ (2) スレッドローカルスコープ (3) モジュールローカルスコープ (4) グローバルスコープ 例えば、次の例では、関数 f1 内での変数 X の代替値は、その関数ローカルスコープ にはないので、スレッドローカルスコープ内の $DefValue になり、関数 f2 内での変数 Y の代替値は、その関数ローカルスコープ内に設定されている DefValue になります。
function f1() { print X; } function f2() { DefValue = "代替値0"; print Y; } $DefValue = "代替値1"; ^DefValue = "代替値2"; ::DefValue = "代替値3"; f1(); f2();
これを実行すると、次の通り、プリントされます。 代替値1 代替値0 代替値による例外処理が不要になった際には、DefValue の箱を削除することによって、 その代替値の例外処理を無効できます。例えば、
DefValue = 0; // これ以降、代替値による例外処理が有効 print A; delete DefValue; // これ以降、代替値による例外処理は無効 print A;
では、最初の print 文の実行で 0 がプリントされますが、2回目の print 文の実行で 発生するエラーの代替は無効なので、そのスレッドは異常終了します。 (2)代替代入先 代入文で、代入先が不正な場合、大抵はコンパイル時にエラーになります。プログラム の実行時に、代入先が不在の場合、通常は新規作成されます。そのため、代入先が不正/ 不在で、例外が発生することは、あまりありません。しかし、例えば、 A::B = 0; の実行で、スコープ規定演算子 :: の左辺の箱 A が、不在、又は、スコープを持たない 箱の場合には、例外が発生します。 このような代入先が不在/不正時に発生する例外に対処するために、「代替代入先」が 設定できるようになっています。代替代入先を設定しておけば、この例外発生時に、その 代入先の代わりに、代替代入先に設定されている代入先へ、その代入文の右辺の評価値が 代入されて、プログラムの実行が継続します。 代替代入先は、DefDest という名前の箱に設定しておきます。この DefDest 箱の格納 スコープとその検索順は、前述の DefValue 箱の場合と同様です。 DefDest 箱の中身が参照の場合、その参照先が、実際の代替代入先になります。また、 その中身が参照でない場合は、DefDest 箱自身が、代替代入先になります。 次に、代替代入先の例を示します。
A = 0; DefDest := A; // 実際の代替代入先は、箱 A になる X::Y = 123; // 代入先が不在/不正のため、例外が発生する print "DefDest=" : DefDest, "A=" : A; DefDest := null; // 実際の代替代入先は、DefDest 自身になる X::Y = 456; // 代入先が不在/不正のため、例外が発生する print "DefDest=" : DefDest, "A=" : A;
これを実行すると、次の通り、プリントされます。 DefDest=123, A=123 DefDest=456, A=123 なお、代替代入先による例外処理を無効にするには、各スコープ内の DefDest 箱を、 削除します。これは、前述の DefValue 箱等の場合と同様です。 (3)代替関数 次のどれかの実行で対象となる箱が不在/不正の時には、例外が発生します。 ・関数コール(標準型、リレー型、代入型、コマンド型) ・クラスインスタンス生成 ・メンバー関数コール ・純粋配列要素の読み出し ・DLL関数コール この例外に対処するために、「代替関数」が設定できるようになっています。代替関数を 設定しておけば、このような例外発生時に、その不在/不正の箱に代わって、代替関数が コールされて、プログラムの実行が継続します。 代替関数の返値は、その不在/不正の箱に対する操作結果の代わりに使われます。この 返値のデータ型は任意です。代替関数は、DefFunc という名前の箱に設定しておきます。 この DefFunc 箱の格納スコープとその検索順は、前述の DefValue 箱の場合と同様です。 次に、未定義関数を標準型とリレー型でコールした場合の代替関数の例を示します。
DefFunc = function( x ) { return ##代替関数: 引数=${x}##; }; print fff( 123 ); print 456'ggg;
この実行では、最初の print 文内で関数 fff を標準型でコールしていますが、この関数 は存在しないので、DefFunc に設定されている関数が代わりにコールされます。そのため、 ここでは、「代替関数: 引数=123」とプリントされます。また、2番目の print 文内で、 関数 ggg をリレー型でコールしていますが、この関数もないので、代替関数が呼ばれて、 ここでは、「代替関数: 引数=456」とプリントされます。 今度は、未定義関数のメンバー関数をコールした場合の代替関数の例を示します。
^DefFunc = function( v ) { print "代替メンバー関数: " : .X, .Y, v; }; class C { .X = 1; .Y = 2; } C.f( 3 );
この実行では、クラス C のメンバー関数 f がコールされていますが、この関数は未定義 なので、代替関数 DefFunc の関数が代わりにコールされます。この時、対象の関数 f の 引数( この場合 3 )とメンバースコープ( この場合、クラス C 内 )は、代替関数に そのまま引き継がれます。そのため、この代替関数によってプリントされるのは、 代替メンバー関数: 1, 2, 3 となります。 本言語では、クラスインスタンス生成、メンバー関数コール、純粋配列要素の読み出し、 DLL関数コールは、標準型の関数コールと、表記上同じ書式になります。そのために、 これらを区別するには、その対象を特定する必要がありますが、その対象が不在/不正の 場合には、それができないので、そのエラー代替はどれも、代替関数のコールになります。 代替関数による例外処理を無効にするには、各スコープ内の DefFunc 箱を削除します。 これは、DefValue 箱の場合と同様です。また、代替関数の場合には、DefFunc の中身が、 関数でなければ、代替関数による例外処理は行なわれません。そのため、例えば、 DefFunc = null; としても、代替関数による例外処理は無効になります。 (4)代替演算 演算子を使う演算で、不正な演算が行なわれた場合に、例外が発生します。この例外に 対処するために、「代替演算関数」と「代替演算値」が設定できるようになっています。 代替演算関数を設定しておけば、不正演算の例外発生時、その関数が呼ばれて、その返値 が、その演算結果の代わりに使われます。また、代替演算値を設定しておけば、不正演算 の例外発生時、その演算結果の代わりにその代替演算値が使われます。いずれの場合でも、 その代替によって、プログラムの実行が継続します。代替演算関数と代替演算値の両方が 設定されている場合、代替演算関数の方が優先されます。 不正演算に対するこのような代替演算が行なわれるのは、次の演算子に限られます。
二項演算子 + - * / % > < >= <= == != && || & | ^ << >> += -= *= /= %= &= |= ^= <<= >>= 前置演算子 + - ~ ++ -- 後置演算子 ++ --
なお、この中に、真偽判定に関連した演算子がないのは、真偽判定演算では、どのような オペランドでも不正としては扱われないからです。 代替演算関数は、上記のどの演算子にも共通に適用される関数と、各演算子ごとに個別 に適用される関数があります。共通の代替演算関数は、DefOpFunc という名前の箱に設定 しておきます。一方、個別の代替演算関数は、DefOpF["op"] という名前の箱に設定して おきます。但し、op は、各演算子の表記そのものになります。例えば、+ 演算子専用の 代替演算関数は、DefOpF["+"] となり、>> 演算子専用の代替演算関数は、DefOpF[">>"] となります。ある演算子に対して、共通と個別の両方の代替演算関数が設定されていると、 個別の代替演算関数の方が優先されます。代替演算関数の返値のデータ型は任意です。 次に、代替演算関数の例を示します。
DefOpF["+"] = function( a, b ) { return ##専用の代替演算関数: ${a} + ${b}##; }; DefOpF["-"] = function( a, b ) { return ##専用の代替演算関数: ${a} - ${b}##; }; DefOpFunc = function( a, b ) { return ##共通の代替演算関数: ${a} ${$err_info} ${b}##; }; A = 1; B.x = -1; C = 0; print A + "文字列"; // 不正演算: 数値 + 文字列 print A - null; // 不正演算: 数値 - null print A * B; // 不正演算: 数値 * 複合箱 print A / C; // 不正演算: 0除算
これを実行すると、次の通り、プリントされます。 専用の代替演算関数: 1 + 文字列 専用の代替演算関数: 1 - <null> 共通の代替演算関数: 1 * <container> 共通の代替演算関数: 1 / 0 代替演算値には、上記のどの演算子にも共通に適用される値と、各演算子ごとに個別に 適用される値があります。共通の代替演算値は、DefOpValue という名前の箱に、個別の 代替演算値は、DefOpV["op"] という名前の箱に、設定しておきます。op は、各演算子の 表記そのものになります。例えば、+ 演算子専用の代替演算関数は、DefOpV["+"] となり、 >> 演算子専用の代替演算関数は、DefOpV[">>"] となります。ある演算子に対して、共通 と個別の両方の代替演算値が設定されていると、個別の代替演算値の方が優先されます。 代替演算値のデータ型は任意です。 次に、代替演算値の例を示します。
DefOpV["+"] = "+ 演算子専用の代替値"; DefOpV["-"] = "- 演算子専用の代替値"; DefOpValue = "共通の代替演算値"; A = 1; B.x = -1; C = 0; print A + "文字列"; // 不正演算: 数値 + 文字列 print A - null; // 不正演算: 数値 - null print A * B; // 不正演算: 数値 * 複合箱 print A / C; // 不正演算: 0除算
これを実行すると、次の通り、プリントされます。 + 演算子専用の代替値 - 演算子専用の代替値 共通の代替演算値 共通の代替演算値 なお、上記の代替演算関数や代替演算値の各箱を格納するスコープと、その検索順は、 前述の DefValue 箱の場合と同様です。また、代替演算関数や代替演算値の箱を削除する ことによって、その箱に対応する各代替演算の例外処理を無効にできます。 (5)代替ラベル goto 文の実行時に、その行き先の可変ラベルが見つからない場合、例外が発生します。 また、call 文の実行では、その行き先の固定/可変ラベルが見つからない場合、例外が 発生します。それに対処するために、「代替ラベル」が設定できるようになっています。 代替ラベルを設定しておけば、このような例外が発生した時、その不在ラベルの代わりに 代替ラベルが使用されて、プログラムの実行が継続します。 代替ラベルは、goto 文の場合、DefLabel という名前を、call 文の場合、DefSubr と いう名前を使います。これらは、固定ラベルとして使用することもできますし、ラベル名 を格納するラベル変数としても使えます。ラベル変数の場合、その中身の文字列が、実際 の代替対象のラベル名になります。固定ラベルの代替ラベルと、ラベル変数の代替ラベル の両方がある場合、ラベル変数の方が優先されます。 goto 文の実行で例外が発生した時は、まず、DefLabel がラベル変数として設定されて いるかどうか確認されます。つまり、DefLabel という名前の箱が、前述の DefValue 箱 の場合と同様の各スコープで検索されます。もし、その箱が見つかり、その中身が文字列 であり、その文字列のラベル名が、現実行中の関数内にあれば、制御はそのラベルへ移行 します。そのような箱が、各スコープのどこにも見つからなければ、今度は、DefLabel が固定ラベルとして設定されているかどうかが確認されます。つまり、DefLabel という 名前のラベルが、現実行中の関数内にあれば、制御はそのラベルへ移行します。 call 文の実行で例外が発生した時も、goto 文の場合とほぼ同じですが、DefLabel で はなく、DefSubr が対象になります。また、call 文の場合、サブルーチンのラベル名が、 現実行中の関数内で見つからない場合には、現実行中の関数が属するモジュール内の暗黙 のメイン関数内も検索されます。 固定ラベルの DefLabel と DefSubr は、プログラムの実行中には変更できませんが、 ラベル変数の DefLabel と DefSubr は、プログラムの実行中に、その中身を書き換える ことができます。また、それを削除する、あるいは、ラベル名に該当しないデータを設定 することによって、ラベル変数によるラベル代替を、無効にすることもできます。 次に、DefLabel を使った例を示します。
i = 0; LOOP: goto Entry[ ++i ]; Entry[1]: print "春"; goto LOOP; Entry["秋"]: print "秋"; delete DefLabel; goto LOOP; END: return; DefLabel: switch( i ) { case 2: print "夏"; DefLabel = :Entry["秋"]; break; case 4: print "冬"; DefLabel = :END; break; } goto LOOP;
これを実行すると、次の通り、プリントされます。 春 夏 秋 冬 ここで、「春」をプリントする所へは、例外の発生なしにジャンプします。「夏」と「冬」 をプリントする所へは、固定ラベルの DefLabel が代替ラベルとなってジャンプします。 「秋」をプリントする所と END ラベルへは、ラベル変数の DefLabel の中身の文字列が 代替ラベルとなってジャンプします。 次に、DefSubr を使った例を示します。
for( i = 1 ; i <= 4 ; i++ ) call Entry[ i ]; return; Entry[1]: print "春"; back; Entry["秋"]: print "秋"; delete DefSubr; back; DefSubr: switch( i ) { case 2: print "夏"; DefSubr = :Entry["秋"]; break; case 4: print "冬"; DefSubr = :END; break; } back;
これを実行すると、前と同じ内容が、プリントされます。ここで、「春」をプリントする サブルーチンへは、例外の発生なしに来ます。「夏」「冬」をプリントするサブルーチン へは、固定ラベルの DefSubr が代替ラベルとなって来ます。「秋」をプリントするサブ ルーチンへは、ラベル変数の DefSubr の中身の文字列が代替ラベルとなって来ます。 ●エラーワープ 既に述べたように、プログラム実行時にエラーが発生した時には、まず、エラー情報が 設定されて、次に、エラー代替が確認され、それが設定されていれば、その処理に移行し ますが、エラー代替が設定されていなければ、「エラーワープ」が確認されます。その際、 に、エラーワープが設定されていれば、そのワープ先に制御が移行します。エラーワープ が設定されていなければ、そのスレッドの実行は異常終了します。 エラーワープ先の設定には、「固定ラベル」と「ラベル変数」のどちらか、又は、その 両方が使えます。これには、次のように、例外処理の各場合に応じて、各々特別な名前が 割り当てられています。同じ例外処理の場合には、この両者の名前は同じです。
OnError 全エラー共通(但し、以下の個別エラーワープの設定の方が優先) OnValueError 不正読出値の例外処理で、代替値 DefValue がない場合 OnDestError 不正代入先の例外処理で、代替代入先 DefDest がない場合 OnFuncError 不正関数コールの例外処理で、代替関数 DefFunc がない場合 OnOpError 不正演算の例外処理で、その代替演算がない場合(全演算共通) OnOpErr["op"] 不正演算の例外処理で、その代替演算がない場合(各演算個別) ( 左記の op は、各演算子の表記そのものになります。) OnLabelError 不正行先ラベルの例外処理で、代替ラベル DefLabel がない場合 OnSubrError 不正サブルーチンの例外処理で、代替ラベル DefSubr がない場合 OnFileError ファイル(バッファ)関連のエラーが発生した場合 OnFileEnd ファイル(バッファ)の読み出し等でその終端を検知した場合
エラーワープは、システムが例外処理時に自動的に行なうワープですが、この動作は、 ユーザーが「warp 文」で行なうワープと、基本的に同様です。例えば、上記の OnError のエラーワープでは、システムが行なうのと、ユーザーが、 warp OnError; を実行するのと、基本的に同様の動作になります。このワープの動作の詳細に関しては、 「制御の流れ(ジャンプ系)」の章の「warp 文」の節で述べています。そのため、ここ では、繰り返しになる部分の説明を省略しています。 エラーワープ状態フラグ $err_state は、エラーワープが起動された時に、整数値 1 に設定されます。それ以外の時は、この変数に対して何も行なわれないので、この変数は、 存在していないか、または、前の値のままです。この変数が存在してその値が 0 以外の 整数値の時には、エラーワープは決して起動されません。これによって、エラーワープの 二重起動を防止しています。エラーワープが起動されてから、再度起動されてもよい状態 になった時には、この変数の値を整数値 0 に設定するか、または、この変数自身を削除 する必要があります。 次の文のブロック内で、エラーワープが起動されると、そのブロックを抜けたことに なります。 ・scope 文 ・構造体設定文 ・クラス設定文 また、これらのブロック内に、エラーワープ先のラベルを設定することはできません。 (例1)全エラー共通対応の固定ラベル OnError へのエラーワープ
A = B; // ここで例外発生 → 固定ラベルの OnError へエラーワープ print "ここには来ない!"; return; OnError: // エラーワープのエントリー print "固定ラベルへエラーワープしました!"; return;
これを実行すると、「固定ラベルへエラーワープしました!」とプリントされます。なお、 「ここには来ない!」はプリントされません。 (例2)全エラー共通対応のラベル変数 OnError へのエラーワープ
OnError = :ErrorHandler; // エラーワープ先のラベルをラベル変数に設定 A = B; // ここで例外発生 → ラベル変数の中身のラベルへエラーワープ print "ここには来ない!"; return; ErrorHandler: // エラーワープのエントリー print "ラベル変数の中身のラベルへエラーワープしました!"; return; OnError: // エラーワープのエントリー print "固定ラベルへエラーワープしました!"; return;
これを実行すると、「ラベル変数の中身のラベルへエラーワープしました!」とプリント されます。なお、「ここには来ない!」はプリントされません。また、ラベル変数の方が 固定ラベルよりも優先されるので、「固定ラベルへエラーワープしました!」はプリント されません。 (例3)コール先の関数内で例外発生 → 固定ラベルへのエラーワープ
F1(); // このコール先の関数内でコールした関数内で例外が発生する print "ここには来ない!"; return; OnError: print "固定ラベルへのエラーワープ!"; return; function F1() { F2(); // このコール先の関数内で例外が発生 print "F1: ここには来ない!"; } function F2() { A = B; } // ここで例外発生!
これを実行すると、「固定ラベルへのエラーワープ!」だけがプリントされます。その他 がプリントされることはありません。 (例4)コール先の関数内で例外発生 → 変数ラベルによるエラーワープ
OnError = :MainErrorHandler; // エラーワープ先のラベルをラベル変数に設定 F1(); // このコール先の関数内とそのコール先の関数内で例外が発生する return; // ここに来ることはない MainErrorHandler: // ラベル変数によるエラーワープのエントリー print "Main: ラベル変数によるエラーワープ!"; return; OnError: //(ラベル変数の方が優先のためここには来ない) print "Main; 固定ラベルによるエラーワープ!"; return; function F1() { OnError = :F1_ErrorHandler; // エラーワープ先をラベル変数に設定 F2(); // このコール先の関数内で例外が発生する return; // ここに来ることはない F1_ErrorHandler: // ラベル変数によるエラーワープのエントリー print "F1: ラベル変数によるエラーワープ!"; delete OnError; // ラベル変数によるエラーワープ禁止 $err_state = 0; // エラーワープを再起動可に F2(); // このコール先の関数内で例外が発生する } function F2() { A = B; } // ここで例外発生!
これを実行すると、次の通り、プリントされます。 F1: ラベル変数によるエラーワープ! Main: ラベル変数によるエラーワープ! (例5)各個別エラー対応の固定ラベルへのエラーワープ
for( i = 1 ; i <= 7 ; i++ ) { switch( i ) { case 1: print A; // 読み出し対象の箱がない → 例外発生 case 2: X::Y = 1; // 代入先が不在/不正 → 例外発生 case 3: print f(0); // コール先の関数がない → 例外発生 case 4: print i + "abc"; // 不正演算 → 例外発生 case 5: print i - null; // 不正演算 → 例外発生 case 6: goto Entry[i]; // 行先のラベルがない → 例外発生 case 7: call Entry[i]; // コール先のサブルーチンがない → 例外発生 } NEXT: $err_state = 0; // エラーワープを再起動可に } print "プログラム終了"; return; OnValueError: print i: ": OnValueError ..."; goto NEXT; OnDestError: print i: ": OnDestError ..."; goto NEXT; OnFuncError: print i: ": OnFuncError ..."; goto NEXT; OnOpError: print i: ": OnOpError ..."; goto NEXT; OnOpErr["-"]: print i: ": OnOpErr[\"-\"] ..."; goto NEXT; OnLabelError: print i: ": OnLabelError ..."; goto NEXT; OnSubrError: print i: ": OnSubrError ..."; goto NEXT;
これを実行すると、次の通り、プリントされます。 1: OnValueError ... 2: OnDestError ... 3: OnFuncError ... 4: OnOpError ... 5: OnOpErr["-"] ... 6: OnLabelError ... 7: OnSubrError ... プログラム終了 (例6)各個別エラー対応のラベル変数へのエラーワープ
OnValueError = :L1; OnDestError = :L2; OnFuncError = :L3; OnOpError = :L4; OnOpErr["-"] = :L5; OnLabelError = :L6; OnSubrError = :L7; for( i = 1 ; i <= 7 ; i++ ) { switch( i ) { case 1: print A; // 読み出し対象の箱がない → 例外発生 case 2: X::Y = 1; // 代入先が不在/不正 → 例外発生 case 3: print f(0); // コール先の関数がない → 例外発生 case 4: print i + "abc"; // 不正演算 → 例外発生 case 5: print i - null; // 不正演算 → 例外発生 case 6: goto Entry[i]; // 行先のラベルがない → 例外発生 case 7: call Entry[i]; // コール先のサブルーチンがない → 例外発生 } NEXT: $err_state = 0; // エラーワープを再起動可に } print "プログラム終了"; return; L1: print i: ": OnValueError ..."; goto NEXT; L2: print i: ": OnDestError ..."; goto NEXT; L3: print i: ": OnFuncError ..."; goto NEXT; L4: print i: ": OnOpError ..."; goto NEXT; L5: print i: ": OnOpErr[\"-\"] ..."; goto NEXT; L6: print i: ": OnLabelError ..."; goto NEXT; L7: print i: ": OnSubrError ..."; goto NEXT;
これを実行すると、(例5)と同じ内容が、プリントされます。 (例7)エラーワープによるクラスインスタンスの自動破棄
class ^C { function Construct( v ) { print "構築: V=" : .V = v; } function Destruct() { print "破棄: V=" : .V; } } F1(); // このコール先の関数内でコールした関数内で例外が発生する print "ここには来ない!"; return; OnError: print "Main: 固定ラベルによるエラーワープ!"; return; function F1() { C1 = ^C( 1 ); C2 = ^C( 2 ); print "F1: -----------"; F2(); // このコール先の関数内で例外が発生する // この時のエラーワープで、インスタンス C1, C2 は破棄される print "F1: ここには来ない!"; } function F2() { A = B; // ここで例外発生 → メイン関数の OnError へエラーワープ }
これを実行すると、次の通り、プリントされます。 構築: V=1 構築: V=2 F1: ----------- 破棄: V=2 破棄: V=1 Main: 固定ラベルによるエラーワープ! (例8)クラス設定ブロック内での例外発生 → エラーワープ
class ^C { .X = 1; .Y = 2; .Z = A; // ここで例外発生 → 固定ラベルの OnError へエラーワープ } print "ここには来ない!"; return; OnError: // ここにエラーワープで来た時は、クラス設定ブロックを抜けた状態になっている do ^C'each with v { print v'name : "=" : v; }; return;
これを実行すると、次の通り、プリントされます。 X=1 Y=2 ●システム管理エラー処理のまとめ 今までに述べた、本言語システムが管理するエラー代替とエラーワープを、次の表に まとめて示します。
エラー代替 | エラーワープ先 | エラーの種類 |
---|---|---|
(なし) | OnError | 全エラー共通 |
DefValue | OnValueError | 読み出し対象の箱が不在/不正 |
DefDest | OnDestError | 代入先が不在/不正 |
DefFunc | OnFuncError | 関数コール先が不在/不正 |
DefOpFunc DefOpValue | OnOpError | 不正演算(全演算子共通) |
DefOpV["op"] DefOpF["op"] | OnOpErr["op"] | 不正演算(各演算子個別) |
DefLabel | OnLabelError | 行き先のラベルが不在/不正 |
DefSubr | OnSubrError | サブルーチンコールのラベルが不在/不正 |
(なし) | OnFileError | ファイル関連エラー |
(なし) | OnFileEnd | ファイル終端検知 |
$OnException = :MainExceptionEntry; F1(); F2(); F3(); return; MainExceptionEntry: print "MainExceptionEntryに来ました! --- 例外データ=" : $ExceptionData; return; function F1() { SomeFunc( "F1" ); // このコール先で OnException へのワープ起動 return; OnException: // 固定ラベルの例外処理エントリー print "F1_ExceptionEntry に来ました! --- 例外データ=" : $ExceptionData; } function F2() { OnException = :F2_ExceptionEntry; SomeFunc( "F2" ); // このコール先で OnException へのワープ起動 return; F2_ExceptionEntry: // ラベル変数 OnException の示す例外処理エントリー print "F2_ExceptionEntry に来ました! --- 例外データ=" : $ExceptionData; } function F3() { SomeFunc( "F3" ); // このコール先で OnException へのワープ起動 // この関数内には、このワープに対するエントリーがないので、 // ワープ先は、ラベル変数 $OnException の示すエントリーになる print "ここには来ない!"; return; } function SomeFunc( v ) { $ExceptionData = v; // 例外データを設定 warp OnException; // 例外処理のワープ起動 }
このプログラムを実行すると、次の通り、プリントされます。 F1_ExceptionEntry に来ました! --- 例外データ=F1 F2_ExceptionEntry に来ました! --- 例外データ=F2 MainExceptionEntryに来ました! --- 例外データ=F3 ●補足説明 try 〜 throw 〜 catch による例外処理は、本言語の例外処理と比較して、以下の短所 があります。 ・代替処理ができない ・throw された例外が catch される場所を動的に変更できない ・例外処理の有効化/無効化のタイミングを動的に切り換えられない ・例外処理の対象になる範囲を動的に変更できない 一方、本言語の例外処理では、try 〜 throw 〜 catch による例外処理のように、送出 された例外オブジェクトのクラス別の捕捉はできません。また、ラベルの使用が必須です。