ワードが処理できないエラー状態を検出した場合、 例外を投げる(throw
)ことができます。 最も単純なケースでは、
これによりプログラムが終了し、適切なエラーが報告されます。
throw
( y1 .. ym nerror – y1 .. ym / z1 .. zn error ) exception “throw”
nerror が 0 の場合は、 それを drop して続行します。 それ以外の場合は、 動的に囲んでいる次の例外ハンドラー(next dynamically enclosing exception handler)に制御を移し、 それに応じてスタックをリセットし、 nerror をプッシュします。
fast-throw
( ... wball – ... wball ) gforth-experimental “fast-throw”
軽量の throw
バリエーション: ゼロ以外のみに使用され、 バックトレースを保存したり、 欠落している catch
を扱ったりしません。
throw
は、スタック上のセル・サイズのエラー番号数値を消費します。 標準 Forth
には事前定義されたエラー番号がいくつかあります(errors.fs 参照)。 Gforth (および他のほとんどのシステム)では、
さまざまなワードによって生成された ior をエラー番号として使用できます(たとえば、 allocate
の一般的な使用法は
allocate throw
です)。 Gforth は、 (適切なエラー報告付きで)独自のエラー番号を定義するための
Exception
というワードも提供します。 このワードの標準 Forth バージョン(ただしエラー・メッセージなし) は
compat/excel.fs
で入手できます。 最後に、 あなた独自のエラー番号(-4095 〜 0
の範囲以外の任意の番号)を使用できますが、 表示されるのは適切なエラー・メッセージではなく、数字のみです。 たとえば、
以下のことを試してみてください:
-10 throw \ 標準で定義済 -267 throw \ システムで定義済 s" my error" exception throw \ ユーザー定義 7 throw \ 思いつくままの任意の番号
exception
( addr u – n ) gforth-0.2 “exception”
n は、 -4095 〜 -256 の範囲内で以前に使用されていなかった throw
値です。
Exception
を連続して呼び出すと、 連続して減少する数値が返されます。 Gforth は文字列 addr u
をエラー・メッセージとして使用します(訳注: 同じエラーメッセージを使いまわすには、 s" hoge err msg" exception
constant hoge-error hoge-error ! などとして得られた hoge-error を使いまわす。 hoge-error
throw 等する)
エラー番号を文字列に変換するためのワード(通常は POSIX の strerror
をモデルにしたもの)がある場合もあります。
以下のワードを使用すると、 これらの文字列を Gforth のエラー処理に取り込むことができます:
exceptions
( xt n1 – n2 ) gforth-1.0 “exceptions”
throw時: xt ( +n -- c-addr u )
は、 0<=n<n1
の範囲のローカルのエラー・コードをエラー・メッセージに変換します。 Exceptions
は、 n2-n1<n3<=n2 の範囲で
n1 個(0 〜 n1)のエラー・コードを予約します。 n2 に、 対応する Gforth エラー・コード(ローカルのエラー番号 0 に対応。
つまり、 0<=n<n1 の範囲のローカルのエラー・コードに 対応する Gforth エラー・コードは n2 <= n3 <
n2+n1)を返します。 (後の時点で)その範囲に対応した Gforth エラー・コード n3 が throw されると、 n2-n3
がプッシュされ(つまりローカルのエラー番号に変換した値をプッシュし)、 xt が実行されて、 xt
がエラー・メッセージを生成します(訳注: minos2/pulse-audio.fs 等で確認。 xt にC-interface ワードをセットし、
外部C言語ライブラリーのエラー・メッセージを gforth 内から表示するのに使っているようだ)
たとえば、 C言語ライブラリーの errno
エラー (および strerror
を使用した変換) がまだ Gforth
で直接サポートされていないとした場合、 以下のようにして strerror
を gforth と結び付けることができます:
' strerror 1536 exceptions constant errno-base : errno-ior ( -- n ) \ n は errno の値に対応する Gforth ior を求めなければならないので、 \ ここでerrno 範囲と Gforth ior の範囲の間で変換する必要があります。 \ ERRNO は Gforth ワードではないため、 \ それにアクセスするには C インターフェイスを使用する必要があります。 errno errno-base over - swap 0<> and ;
C言語の関数(C言語インターフェイス(C interface)を使用)を呼び出し、 その戻り値がエラーが発生したことを示している場合、
errno-ior throw
を実行して、適切なエラー・メッセージ (“Permission denied”
など)を含む例外を生成できます。
フラグが true の場合、 特定の err# でエラーを投げる(THROW
)一般的な慣用句は以下のとおりです:
( flag ) 0<> err# and throw
あなたのプログラムで、 例外をキャッチする例外ハンドラーを提供できます。 例外ハンドラーを使用すると、 問題を修正したり、
一部のデータ構造をクリーンアップして例外を次の例外ハンドラーに投げたり(throw)することができます。 throw
は動的に最も内側の例外ハンドラー(the dynamically innermost exception
handler)にジャンプすることに注意してください。 システムの例外ハンドラーは最も外側にあり、
エラーを出力してコマンド・ラインの通訳(interpretation)を再開するだけです(または、 バッチ・モード(つまり、
シェル・コマンド・ラインの処理中)では Gforth を終了します)。
例外をキャッチする標準 Forth での方法は catch
です:
catch
( x1 .. xn xt – y1 .. ym 0 / z1 .. zn error ) exception “catch”
xt を実行します。 実行から正常に戻った場合、 catch
はスタックに 0 をプッシュします。 throw
を介して実行が戻った場合、 すべてのスタックは catch
へ入る時点の深さにリセットされ、 TOS (xt の位置) は
throw コードに置き換えられます。
nothrow
( – ) gforth-0.7 “nothrow”
再 throw しない catch
または endtry
の後ろでこれ (または標準のシーケンス [']
false catch 2drop
) を使用します。 これにより、 次の throw
でバックトレースが確実に記録されます。
例外ハンドラーの最も一般的な使用法は、 エラーが発生したときに状態をクリーンアップすることです。 例:
base @ >r hex \ actually the HEX should be inside foo to protect \ against exceptions between HEX and CATCH ['] foo catch ( nerror|0 ) r> base ! ( nerror|0 ) throw \ pass it on
myerror
というエラー番号を処理するための catch
の使用は以下のようになります:
['] foo catch CASE myerror OF ... ( do something about it ) nothrow ENDOF dup throw \ default: pass other errors on, do nothing on non-errors ENDCASE
コードを別のワードでくるむ要があるのは面倒な場合が多いため、 Gforth では代替構文を提供しています:
TRY code1 IFERROR code2 THEN code3 ENDTRY
これは、 code1 を実行しします。 code1 が正常に完了すると、 code3 の実行へ続きます。 code1
または、 endtry
より前で例外があった場合、 スタックは try
時の深さにリセットされ、 throw
された値をデータ・スタックにプッシュし、 code2 の実行に続き、 そして、 最終的に code3 に到達します。
try
( compilation – orig ; run-time – R:sys1 ) gforth-0.5 “try”
例外キャッチ領域の開始
endtry
( compilation – ; run-time R:sys1 – ) gforth-0.5 “endtry”
例外キャッチ領域の終わり
iferror
( compilation orig1 – orig2 ; run-time – ) gforth-0.7 “iferror”
例外処理コードを開始します(try
と endtry
の間に例外がある場合に実行されます)。 この部分は
then
で終了する必要があります。
code2 が必要ない場合は、 iferror then
の代わりに restore
を記述できます:
TRY code1 RESTORE code3 ENDTRY
先程の例をこの構文で身綺麗にしてみます:
base @ { oldbase } TRY hex foo \ now the hex is placed correctly 0 \ value for throw RESTORE oldbase base ! ENDTRY throw
このバリエーションの追加の利点は、 restore
と endtry
の間の例外(たとえば、 ユーザーが
Ctrl-C を押すことによる例外)でも、 restore
の直後へコードの実行が移ることです。 ゆえに、
いかなる状況であっても base は復元されます。
ただし、 このコード自体が例外を引き起こさないようにする必要があります。 そうしないと、 iferror
/restore
コードがループします。 さらに、 iferror
/restore
コードで必要なスタックの内容が try
と
endtry
の間のあらゆる場所に存在することも確認する必要があります。 この例では、 これは try
の前にデータをローカル変数(local)に置くことによって実現されます(リターン・スタック上の例外フレーム(sys1)が邪魔なのでリターン・スタックは使用できません)。
この種の使用法は、 Lisp の unwind-protect
と同様のものです。
もし、あなたが、 この例外再開始(exception-restarting)の振る舞いを望まない場合は、 以下のようにしてください:
TRY code1 ENDTRY-IFERROR code2 THEN
code1 に例外がある場合は code2 が実行され、 それ以外の場合は then
の後ろ (または
else
分岐の可能性あり) から実行が続行されます。 これはバージョン 0.7 より前の Gforth では以下の構成要素に該当します
TRY code1 RECOVER code2 ENDTRY
つまり、 この recover
を使用しているコードを直に try ... entry-iferror ... then
へと置き換えることができます。 ただし、 その置き換え作業中に他の try
バリエーションのいずれかを使用した方が良いかどうかも検討することをお勧めします。
移行を容易にするために、 Gforth は 2 つの互換性ファイルを提供します: 1つ目は endtry-iferror.fs で、
古いシステム用に try ... endtry-iferror ... then
構文を提供します(ただし、 iferror
または restore
は提供しません)。 2つ目の recover-endtry.fs は、 新しいシステム上で古い構文の
try ... recover ... endtry
構文を提供するので、 古いプログラムを実行するための一時しのぎとして使用できます。
どちらのファイルもどのシステムでも動作します(システムが、 実装する構文を既に定義済みの場合は何も行わないだけです)。 そのため、
古いシステムと新しいシステムを混在させて使用している場合でも、 これらのファイルのいずれかを無条件に require
することができます。
restore
( compilation orig1 – ; run-time – ) gforth-0.7 “restore”
コードの復元(restore)を開始します。 これは、 例外がある場合と無い場合に行われます。
endtry-iferror
( compilation orig1 – orig2 ; run-time R:sys1 – ) gforth-0.7 “endtry-iferror”
例外キャッチ領域を終了し、 その領域外で例外処理コードを開始します(try
と endtry-iferror
の間に例外がある場合に実行されます)。 この部分は then
(または else
...then
)
で終了する必要があります。
ここで、 エラー処理の例を以下に示します:
TRY foo ENDTRY-IFERROR CASE myerror OF ... ( do something about it ) nothrow ENDOF throw \ pass other errors on ENDCASE THEN
プログラミング・スタイル・メモ:
いつものように、 エラーを渡すための throw
の後、 または ENDTRY
の後のいずれか(または、catch
を使用する場合は、エラーを処理するための選択構造の終了後)で、
スタックの深さが静的に明白であることを保証する必要があります。
throw
の代替は 2 つあります: Abort"
は条件付きでエラー・メッセージを提供できます。
Abort
は「中止」(Abort)エラーを生成するだけです。
これらのワードの問題は、 例外ハンドラーが、 異なる abort"
を区別できないことです。 例外ハンドラーにとってはそれらは
-2 throw
のように見えるだけです(標準のプログラムではエラー・メッセージにアクセスできません)。 同様に、
abort
は例外ハンドラーに対して -1 throw
のように見えます。
ABORT"
( compilation ’ccc"’ – ; run-time f – ) core,exception-ext “abort-quote”
f のいずれかのビットがゼロ以外の場合、 -2 throw
の機能を実行し、 例外スタック上例外フレームがない場合は文字列
ccc を表示します。
abort
( ?? – ?? ) core,exception-ext “abort”
-1 throw
.
実行を中止する必要があるほど深刻でない問題の場合は、 警告を表示するだけで済みます。 変数 warnings
を使用すると、
表示される警告の数を調整できます。
WARNING"
( compilation ’ccc"’ – ; run-time f – ) gforth-1.0 “WARNING"”
f がゼロ以外の場合、 警告メッセージとして文字列 ccc を表示します。
warnings
( – addr ) gforth-0.2 “warnings”
以下の警告レベルをセットしてください
0
警告オフ
-1
通常警告オン
-2
初心者警告オン
-3
偏執狂的警告オン
-4
全ての警告をエラーとして扱います(初心者警告を含む)