Gaucheの例外システムは次の3つのコンポーネントから構成されています。 (1)例外状況が発生したことを通知する方法、 (2)例外状況をどのように処理するかを指定する方法、 (3)例外状況を知らせたコードとそれを処理するコードがやりとりするための 標準オブジェクト(コンディション)。
普通これらの3つのコンポーネントは一緒に使われます。 そこで、最初に例を用いて典型的な使い方について説明し、 そのあとでそれぞれの機能について詳しく解説します。
用語について: いくつかの言語では例外(exception)というと、 例外的状況に遭遇したコードとそのハンドラがやりとりをするために用られる オブジェクトのことを指します。Gaucheではそのようなオブジェクトのことを言うときには、 SRFI-35にならってコンディション(condition)を使います。 例外というのは、状況であり、コンディションはそれを記述する 実行時のオブジェクトです。
• 例外処理の概要: | ||
• 例外の通知: | ||
• 例外の処理: | ||
• コンディション: |
最もよくある例外処理のひとつは、組み込みあるいは
ライブラリの手続きから発生した特定のエラーを捕捉するというものです。
guard
マクロがこのような目的の場合に使えます。
コードは以下のような感じになるでしょう。
(guard (exc [(condition-has-type? exc <read-error>) (format #t "read error!") 'read-error] [else 'other-error]) (read-from-string "(abc"))
guard
節の cadr 部は (variable clause …)
という
形式です。この例では、変数は exc
で、2つの節があります。
それぞれの clause は cond
と似た形式になります。
guard
の cddr 部は本体で、式のリストです。この例では、式は
(read-from-string "(abc")
のひとつだけです。
guard
はその本体部を実行するところから始めます。
read-from-string
は構文エラーに出くわすと、<read-error>
型の
エラーを発生させます。guard
フォームがこのエラーを捕捉し、
そのコンディションオブジェクトを変数excに束縛し、excの後の
節を、cond
と同じようにチェックします。この場合、投げられた
コンディションは <read-error>
なので、最初の節のテストを満し、
その節の残りの部分が実行されます。すなわち、"read error!"
が
印字され、シンボル read-error
が返ります。
他の言語を使い慣れていれば、同じパターンであることがわかると思います。
guard
フォームの cddr 部は、C++やJavaの try 節、あるいは、
Common Lisp の handler-case
の cadr 部に似ています。
また、guard
フォームの cdadr 部は、catch
節あるいは、
handler-case
の cddr 部に似ています。
テスト式においては、投げられたコンディションのタイプをチェックする
のが普通です。condition-has-type?
という関数が
SRFI-35 で定義されていますが、これはちょっと冗長です。Gauche の
コンディションクラスは述語のようにも使えるようになっており、上の
式は以下のように書くこともができます。
(guard (exc [(<read-error> exc) (format #t "read error!") 'read-error] [else 'other-error]) (read-from-string "(abc"))
注意事項: 一般的には、投げられたコンディションが特定の
タイプであるかをチェックするのにis-a?
は使えません。
コンディションが合成されたものである可能性があるからです。
合成されたコンディションについての
詳細は コンディション を参照してください。
もし、clause のどのテストも満されず、かつ else
節があたえられて
いなければ、その例外は guard
から「抜け」ます。すなわち、guard
の外側のレベルあるいはトップレベルで処理されることになります。たとえば、
以下の guard
フォームでは、<read-error>
と
<system-error>
としか処理できず、もし、本体が他のタイプのコンディション
を投げてきたら、その外側のレベルで処理しなければなりません。
(guard (exc [(<read-error> exc) (handle-read-error)] [(<system-error> exc) (handle-system-error)]) body ...)
guard
および他の低レベルの例外処理の構成についての詳細は
例外の処理 を参照してください。
例外を通知する一般的な方法は raise
手続きを使うことです。
(raise condition)
conditionにはどんなオブジェクトでも渡すことができます。
それをどのように解釈するかはひとえに例外ハンドラにかかってます。
もし、コンディションとして整数があがってくるというのが判っていれば、
guard
で以下のように捕捉することができます。
(guard (exc [(integer? exc) 'raised]) (raise 3))
とはいうものの、<condition>
あるいはそのサブクラスのインスタンスを
使うのが好ましいというのが通例です。condition
マクロはコンディション
オブジェクトを作成するのに使えます。以下の例は、いくつかのスロット値をもち
それらを発生させるコンディションの作りかたを示したものです。
;; create and raise an error condition (raise (condition (<error> (message "An error occurred.")))) ;; create and raise a system error condition (raise (condition (<system-error> (message "A system error occurred.") (errno EINTR))))
condition
マクロおよび、どのようなコンディションクラスが用意
されているかの詳細については コンディション を参照してください。
最も一般的なコンディションのタイプはエラーコンディションなので、
error
および errorf
という便利な手続きが
用意されています。これらはメッセージ付きのエラーコンディションを
生成し、それを発生させます。
;; `error' concatenates the arguments into a message. (unless (integer? obj) (error "Integer expected, but got:" obj)) ;; `errorf' uses format to create a message. (unless (equal? x y) (errorf "~s and ~s don't match" x y))
いくつかの言語での例外を投げる機構、たとえば、
C++やJavaの throw
はその継続を破棄します。これとは違い
Schemeの raise
はその呼び出し元へ戻ることができます。もし、
raise
で元へもどらないで欲しいのなら、簡便な方法としては、
常にエラーコンディションの一つをわたるようにするというのがあります。
そうすると Gauche では raise
は戻らないことを保証します。
raise
の詳細については、例外の通知を参照してください。
註: R7RSでは少し違ったセマンティクスを採用しています。
raise
は継続不可能な例外を投げるものとして(もし例外ハンドラから
制御が戻ってきたら、別のエラーを報告します)、
別に継続可能な例外を投げるraise-continuable
という手続きを
設けました。R7RS環境にいるときは、本項のraise
ではなく
R7RS互換のraise
が見えるようになっています。
独自のコンディションクラスを定義することが可能で、そうすることで、 アプリケーション固有の情報を例外が発生した点からハンドラへ渡すことが できます。
Gauche のフレームワーク(SRFI-35)に適合させるためには、新しく定義する
コンディションクラスは組み込みの <condition>
クラスあるいは
その子孫を継承し、また、メタクラス <condition-meta>
のインスタンスであることが望まれます。
可搬性を増すと同時に上の慣例を確実にするための方法のひとつは、
define-condition-type
マクロを使うことです。これは、
srfi.35
で定義されています。
(define-condition-type <myapp-error> <error> myapp-error? (debug-info myapp-error-debug-info) (reason myapp-error-reason))
これは、(Gauche内のクラス)<myapp-err>
を定義するもので、
このクラスにはmyapp-error?
という述語とアクセサのあるスロット
があります。こうすれば、以下のようなコードで新しいコンディション
型が使えます。
(guard (exc [(myapp-error? exc) (let ([debug-info (myapp-error-debug-info exc)] [reason (myapp-error-reason exc)]) ... handle myapp-error ...)]) ... ... (if (something-went-wrong) (raise (condition (<myapp-error> (debug-info "during processing xxx") (reason "something went wrong"))))) ... ... )
SRFIとの互換性が重要でないなら、Gaucheの拡張されたerror
手続きを
使うと<error>
のサブタイプであるコンディションを投げるコードを
より簡潔に書くことができます。
(if (something-went-wrong) (error <myapp-error> :debug-info "during processing xxx" :reason "something went wrong"))
Gauche のオブジェクトシステムでコンディション型がどのように実装されているかは
define-condition-type
マクロの解説を参照してください。
最も良くある例外ケースはエラーです。単純なエラーを通知するために、
ふたつの簡単な関数が用意されています。
複合コンディションを通知する必要がある場合は
下で説明するraise
手続きを使って下さい。
[R7RS+ base][SRFI-23+]
エラーを通知します。最初の形式は、
stringとarg …からなるメッセージを持つ
<error>
コンディションを作成それをraise
します。
この形式はR7RS及びSRFI-23のerror
と互換です。
gosh> (define (check-integer x) (unless (integer? x) (error "Integer required, but got:" x))) check-integer gosh> (check-integer "a") *** ERROR: Integer required, but got: "a" Stack Trace: _______________________________________
2番目の形式は<error>
以外のエラーコンディションを通知したいときに
使います。condition-typeはコンディションタイプ
でなければなりません (コンディションタイプについてはコンディションを
参照して下さい)。その後に、キーワードと値のリストを与えることで
コンディションのスロットを初期化することができます。また、その後に
メッセージを構成する文字列と他のオブジェクトのリストを与えることができます。
(define-condition-type <my-error> <error> #f (reason) (priority)) ... (unless (memq operation *supported-operations*) (error <my-error> :reason 'not-supported :priority 'urgent "Operation not supported:" operation)) ...
error
に似ていますが、エラーメッセージはformat
によりフォーマット
されます。すなわち、最初のフォームは以下と等価です。
(define (errorf fmt . args) (error (apply format #f fmt args)))
2番目の形式は<error>
以外のエラーコンディションを通知するのに
使えます。condition-type, keyword-argの意味については
error
と同じです。
[SRFI-18][R7RS base] これは、例外事態を通知する基本となるメカニズムです。
この手続きは現在の例外ハンドラを呼び出します。引数conditionは
例外の性質を表現するのに使われ、例外ハンドラに渡されます。
Gaucheの組み込み手続きやライブラリ手続きは常に、<condition>
クラス
もしくはそのサブクラスのインスタンスをconditionとして用いますが、
ユーザは任意のオブジェクトをraise
に渡すこともできます。
渡されたconditionの解釈は例外ハンドラに任されます。
注意事項: いくつかの主流の言語では、例外を「投げる」と制御はそこに
戻りません。Gaucheではraise
から戻って来れるように
セットアップすることが可能です。詳細は例外の処理を
参照してください。
raise
から戻って来ることが無いようにしたい場合は、
<serious-condition>
やそのサブクラスのインスタンスをcondition
としてraise
に渡すのが確実です。
組み込みコンディションのクラス階層についてはコンディションを参照して下さい。
R7RSではやや異なるセマンティクスを採用しています。R7RSのraise
は
決して戻りません—もしハンドラが戻ってきた場合は、別の例外が投げられます。
R7RSはraise-continuable
という別の手続きで、ハンドラから
戻っても良いことを明示します。ポータブルなプログラムでは、
raise
には常に<serious-condition>
かそのサブクラスを
渡すようにするのが良いでしょう。
• 上位レベルの例外処理機構: | ||
• 処理されなかった例外のふるまい: | ||
• 下位レベルの例外処理機構: |
[R7RS base] これはGaucheでのエラー処理の高水準フォームです。
var はシンボルで clause は cond
節と同じ形式です。
つまり、各節は以下の形式のどれかひとつです。
(test expr …)
(test => proc)
最後のclauseは(else expr …)
という形式も許されます。
このフォームは通常の場合には body … を評価し最後の body の式の値を返します。本体の式を評価している最中に例外が 発生した場合、発生した例外を変数 var に束縛し、その後 各節の test 式を評価します。もし、test 式のひとつが 真値を返したとき、その節が上述の最初の形式であれば、対応する expr が評価されます。あるいは、節が二番目の形式であれば、 手続きprocにtestの結果が渡されます。
指定された節の test および expr が評価されるとき、
guard
を呼び出した時点での例外ハンドラが設定されます。つまり、
clause 内部で再び例外が発生した場合、その例外は、外側の
例外ハンドラまたは guard
フォームで処理されます。
もし、どの test も真値を返さず、最後の clause
が
else
節であれば、それに結びつけられた expr が評価されます。
もし、どの test も真値を返さず、else
節がなければ、再び
例外が発生し、外側の例外ハンドラで処理されます。
例外が clause
のどれかで処理された時には、guard
は
それを処理した節の最後の expr の値を返します。
clauseはguard
と同じ動的環境で評価されます。すなわち、
body
中のdynamic-wind
はclauseの評価の前に
巻戻されます。これは低レベル関数のwith-error-handler
や
with-exception-handler
とは異なることに注意してください。
これら低レベル関数では例外ハンドラが呼ばれてから動的環境が巻戻されます。
(let ([z '()]) (guard (e [else (push! z 'caught)]) (dynamic-wind (lambda () (push! z 'pre)) (lambda () (error "foo")) (lambda () (push! z 'post)))) (reverse z)) ⇒ (pre post caught) (guard (e [else (print 'OUTER) #f]) (with-output-to-string (lambda () (print 'INNER) (error "foo")))) ⇒ OUTERを文字列ポートではなくguard実行時の current-output-portに出力。
exprを実行してからcleanup …を実行し、exprの結果を返します。
expr内で継続不可能な例外が挙がった場合、その例外がunwind-protect
フォー
ムを抜ける前に、cleanup …が実行されます。たとえば、以下のコードで
はなにも問題が起きなければ、start-motor
、drill-a-hole
、
stop-motor
が、この順で呼ばれます。何か不具合が
start-motor
あるいはdrill-a-hole
で起った場合でも、例外が
unwind-protect
を抜ける前にstop-motor
が呼ばれます。
(unwind-protect (begin (start-motor) (drill-a-hole)) (stop-motor))
cleanupフォームはunwind-protect
と同じ動的環境で評価されま
す。例外がcleanup内で投げられた場合その例外は、
unwind-protect
フォームの外側で処理されることになります。
このフォームはdynamic-wind
と似ていますが、この2つは動作するレイ
ヤが違いますので混同しないようにしてください。
dynamic-wind
は最下位レイヤのもので、現在の例外ハンドラ、現在の入出力
ポート、パラメータなどを管理するのに用います。
dynamic-wind
のbeforeおよびafterのサンクは
対応する制御フローの遷移が起きたときに必ず呼ばれます。
一方、unwind-protect
はGaucheの例外システムの面倒しか見ません。
unwind-protect
のcleanupはexprが正常終了するか
Gaucheの例外を投げたときにのみ呼びだされます。上述の例で、unwind-protect
の外側で
補足された継続が呼ばれ、制御がdrill-a-hole
を抜けると、
cleanupは呼ばれません。制御が再びdrill-a-hole
に戻る可能性
があるからです。たとえば、ユーザレベルのスレッドシステムが
call/cc
で実装されているような場合にこのようなことが起こる可能性
があります。
expr内で捕捉された継続をunwind-protect
の外側で起動することで、
expr内に再び戻ることは可能です。
ただ、ひとたびcleanupが実行されてしまったら、それによってクリーンアップ されたリソースはexpr内から使えなくなっているという可能性に留意してください。 そういったリソースに依存しない計算なら再実行できるので、再起動自体は禁止されていません。
exprから複数回戻った場合(通常の評価でもエラーでも)、cleanupが 実行されるのは最初の時のみです。
このフォームの名前はCommon Lispから取りました。同様のマクロに
try-finally
というような別の名前を使っているSchemeの処理系もあります。
handlerをアクティブなエラーハンドラにし、thunkを実行します。
thunkが正常に戻ったら、その結果が返されます。
thunkの実行中にエラーが通知されたら、エラーを表す例外オブジェクトを
1引数とするhandlerが、with-error-handler
の継続とともに呼ばれます。
すなわち、with-error-handler
は、handlerが返す値を返します。
handlerがエラーを通知したら、それはwith-error-handler
が
呼ばれたときにインストールされていたハンドラにより処理されます。
handlerが実行される場合の動的な環境は、エラーが起きたときのそれと
同じです。thunk内でdynamic-wind
が使われていたら、
そのafterメソッドはhandlerが戻った後、かつwith-error-handler
が
戻る前に呼ばれます。
註: この手続きを直接使うことはもはや推奨されません。guard
の方が
より安全でポータブルだからです。互換性を保つためまだしばらくは
この手続きを残しますが、この手続きを使っているコードをguard
によって
書き直すことを推奨します。「エラー時に後始末をする」というよくある次のような処理は:
(with-error-handler (lambda (e) (cleanup) (raise e)) (lambda () body ...))
次のように書き直すことができます。
(guard (e [else (cleanup) (raise e)]) body ...)
プログラムで定義した例外ハンドラを設定していないところで例外が発生した 場合以下のようなことが起ります。
<uncaught-exception>
でラップされて
スレッドオブジェクトに保存されます。他のスレッドがthread-join!
で
スレッドの結果を取り出そうとした時に、その<uncaught-exception>
が投げられます。
元の捕捉されない例外が起きた時点では何もメッセージなどが表示されないことに注意してください。
詳しくはスレッドプログラミングTipsを参照。
EX_SOFTWARE
(70)で終了します。
上の2と3でのエラーメッセージとスタックトレースは、report-error
手続きによって
出力されています。自分のエラーハンドラ内で同じ情報を出力したければ
これを使うことができます。
投げられたコンディションオブジェクトexnの型とメッセージを表示し、 それからスタックトレースを出力します。REPLでエラーが報告される時の 表示を出しているのがこの手続きです。
raise
は任意のオブジェクトをコンディションとして投げることが出来るので、
exnも<condition>
オブジェクトの
インスタンスである必要はなく、どんな型でも許されます。
report-error
は適切なメッセージを選んで表示します。
出力の行き先は省略可能引数sinkで指定できます。出力ポートを渡せば
そこに出力されます。また、format
と同様に、#t
を渡すことで
現在の出力ポートに、#f
を渡すことで一時的な文字列ポートに出力できます。
#f
を渡した場合、一時的な文字列ポートに出力された文字列が
返り値となります。その他の場合は未定義値が返されます。
sinkが省略されるか、上記以外の値であった場合は
現在のエラーポートが使われます。
内部的に、この手続きはexnの情報を表示するために
print-default-error-heading
および
print-additional-error-heading
を呼び、その後スタックトレースを表示します。
スタックトレースが不要な場合、これらの手続きを直接呼ぶこともできます。
註: 0.9.5の時点で、この手続きはexnが投げられたコンテキストでの
スタックトレースではなく、report-error
自身が呼ばれたコンテキストでの
スタックトレースを表示します。report-error
をエラーハンドラから
直接呼んでいる限りにおいてはあまり違いは出ませんが、一般的に望ましいのは
前者なので、将来的には<condition>
オブジェクトにスタックトレース情報を
つける予定があります。
投げられたコンディションexnについて、デフォルトのエラーレポートの 1行目を出力ポートoutに書き出します。
exnが<condition>
なら、
コンディションクラス名(からブラケットを取り除き、大文字化したもの)と、
コンディションメッセージが出力されます:
*** READ-ERROR: Read error at "foo":line 1: EOF inside a list (starting from line 1)
exnが<condition>
でない場合は、
本来補足されるべきであったコンディションがすり抜けたとみなし、
例えば次のようなメッセージが出力されます:
*** ERROR: unhandled exception: foo
この手続きはreport-error
が使っていますが、
この手続きだけを直接呼び出すことも可能です。
投げられたコンディションexnについて、追加の情報を出力ポートoutに書き出します。
exnが<condition>
でなければ、何も出力されません。
exnが複合コンディションの場合は、まず個々のコンディションに分解されます。
次に、各コンディションのうち<mixin-condition>
を継承していない
コンディションについてreport-additional-condition
メソッドが呼ばれ、
最後に<mixin-condition>
を継承しているコンディションについて
report-additional-condition
メソッドが呼ばれます。
例えば、次のようなエラーレポートがあった場合:
*** UNBOUND-VARIABLE-ERROR: unbound variable: load NOTE: `load' is exported from the following modules: - scheme.r5rs - scheme.load While loading "/home/shiro/src/Gauche/src/../src/ttt.scm" at line 3 While compiling "./tttt.scm" at line 1: (use ttt) While loading "./tttt.scm" at line 1
***
で始まる行)は
print-default-error-heading
が出しています。
残りはprint-additional-error-heading
が出しています。
<unbound-variable-error>
、<load-condition-mixin>
、
<compile-error-mixin>
、そしてもうひとつの
<load-condition-mixin>
の複合コンディションです。
NOTE
から始まる3行は、<unbound-variable-error>
の
report-additional-condition
メソッドが出力しています。
While loading ...
の行は<load-condition-mixin>
の
report-additional-condition
メソッドが出力しています。
While compiling ...
の行は<compile-error-mixin>
の
report-additional-condition
メソッドが出力しています。
この手続きはreport-error
が使っていますが、
この手続きだけを直接呼び出すことも可能です。
コンディションオブジェクトconditionについての追加の情報を
出力ポートoutに出力します。
いくつかの組み込みのコンディションはこのジェネリックファンクションを特定化しています。
具体例は上のprint-additional-error-heading
の項を見てください。
このレイヤはSRFI-18互換のシンプルな例外メカニズムを提供します。
with-error-handler
のような高次元の構造の振る舞いを、
with-exception-handler
を使って上書きすることができます。
これは諸刃の剣であることに注意して下さい。あなたは独自の例外処理 セマンティクスを構築する自由がありますが、Gaucheシステムは何か 間違いがあっても救ってくれません。システムの高次元のセマンティクスを カスタマイズしたいか、他のSRFI-18準拠のコードを移植している場合にのみ、 これらのプリミティブを使って下さい。
[SRFI-18] 現在の例外ハンドラを返します。
[SRFI-226] 現在の例外ハンドラスタックにある例外ハンドラのリストを返します。 リストは新にアロケートされたもので、最後に設定されたハンドラが最初に来ます。
[R7RS base][SRFI-34]
handlerは1引数を取る手続きです。この手続きは、handlerを
現在の例外ハンドラにセットし、thunkを呼び出します。
(この手続きはSRFI-18のwith-exception-handler
と微妙に異なります。)
例外がraise
やerror
で通知されると、
投げられたコンディションを引数としてhandlerが
呼び出し元の動的環境のうち、例外ハンドラだけがwith-exception-handler
呼び出し時点のものに置き換えられた環境で呼び出されます。
註: SRFI-18のwith-exception-handler
では、
例外ハンドラは例外発生箇所と全く同じ動的環境で呼び出されるとしています。
つまり例外ハンドラ中で例外が発生すると、同じハンドラが再び呼ばれます。
これは、with-exception-handler
を最も基本的なレイヤとして、
その上に必要なセマンティクスを実装することが意図されていたからです。
0.9.10まではGaucheの組み込みのwith-exception-handler
はSRFI-18の
セマンティクスで、その上にR7RSのwith-exception-handler
が実装されていました。
しかし、SRFI-18セマンティクスは非常に使い方を間違えられやすかったのです。
ユーザはハンドラ中で発生した例外は外側のハンドラで処理されると思い込みがちでした。
正しくハンドラの置き換えを行わないと、ハンドラ中の例外はハンドラへの無限再帰を引き起こし、
Cのスタックを食いつぶしてsegfaultします。実質的に全ての場合においてユーザが期待するのは
R7RSセマンティクスなので、切り替えることにしました。
例外がerror
により投げられた場合、あるいは投げられたコンディションが
<serious-condition>
を継承していた場合、
handlerから戻ることは禁止されます。もし戻ってしまったら、
ハンドラを外側のものに置き換えた上で別のエラーが投げられます。
したがって、error
の呼び出し元、あるいは<serious-condition>
を
引数としたraise
の呼び出し元は、
その呼び出しが決して戻らないと考えて構いません。
これらの手続きの振る舞いは、次の概念的なSchemeコードによって 説明されるでしょう。
;; 低レベルな例外メカニズムの概念的な実装 ;; %xhは例外ハンドラのリスト (define (current-exception-handler) (car %xh)) (define (raise exn) (let ((prev %xh)) (dynamic-wind (lambda () (set! %xh (cdr %xh))) (lambda () (receive r ((current-exception-handler) exn) (if (uncontinuable-exception? exn) (raise (make-error "returned from uncontinuable exception")) (apply values r)))) (lambda () (set! %xh prev))))) (define (with-exception-handler handler thunk) (let ((prev %xh)) (dynamic-wind (lambda () (set! %xh (cons handler %xh))) thunk (lambda () (set! %xh prev)))))
現在のところGaucheには以下の組み込みコンディションクラスの階層があります。 これは、おおよそのところ SRFI-35 および SRFI-36 のコンディションの階層を 反映したものですが、Gauche風のクラス名になっています。対応する SRFI の コンディションタイプがあるものについては、SRFI でのクラス名も使えます。
<condition> +- <compound-condition> +- <serious-condition> | +- <serious-compound-condition> ; also inherits <compound-condition> +- <message-condition> +- <error> ; also inherits <serious-condition> +- <system-error> +- <unhandled-signal-error> +- <continuation-violation> +- <read-error> +- <io-error> +- <port-error> +- <io-read-error> | +- <io-decoding-error> +- <io-write-error> | +- <io-encoding-error> +- <io-closed-error> +- <io-unit-error> +- <io-invalid-position-error>
いくつかのコンディションが同時に発生することがあることに注意してください。
たとえば、ファイルの読み込がデバイスの欠陥により失敗した場合は、
<system-error>
および <io-read-error>
の両方からなる
エラーとなるでしょう。
このような場合、合成したコンディション (compound condition) が発生します。
したがって、たとえば、<io-read-error>
が投げられたかどうかをチェック
するのに、単に (is-a? obj <io-read-error>)
を使えばよいというわけ
にはいきません。
後述の「コンディション API」の節を参照してください。
すべてのコンディションクラスはこのクラスのインスタンスです。
このクラスは object-apply
を定義していますので、
コンディションクラスは述語として使うことができます。たとえば、
(<error> obj) ≡ (condition-has-type? obj <error>)
合成コンディションを表現します。合成コンディションは
make-compound-condition
を用いて1つ以上のコンディションから
生成することができます。このクラスを直接使ってはいけません。
合成コンディションはcondition-has-type?
に対して、
元のコンディションのどれかが、与えられたタイプなら、#t
を返します。
[SRFI-35]
このクラスのコンディションは無視して先を続けることはできない深刻な
状況のためにあります。特に、このタイプのコンディションを raise
したら、それは元には絶対に戻らないと考えて問題ありません。
深刻なコンディションを含む合成コンディションを表現するための
内部クラスです。<compound-condition>
および
<serious-condition>
の両方を継承しています。
make-compound-condition
は深刻なコンディションを含む複数の
コンディションを渡されると、このクラスを使います。このクラスを
直接使ってはいけません。
[SRFI-35] このクラスはメッセージ付のコンディションを表現し、スロットを一つ もっています。
<message-condition>
: message ¶メッセージ
[SRFI-35]
エラーを表します。<serious-condition>
および
<message-condition>
を継承しています。したがって、
message
スロットを持っています。
注意事項: SRFI-35 の&error
コンディションは &serious
のみを継承し、&message
は継承していません。したがって、
このエラーコンディションにメッセージを付与するためには、
合成コンディションを使わなければなりません。Gauche は
主として過去のバージョンとの互換性を確保するために、
ここで多重継承を用いています。
可搬性のあるコードを書くには、以下のようにメッセージコンディション
付きのエラーコンディションを使うべきです。
(condition (&message (message "Error message")) (&error))
[SRFI-35]
これらはSRFI-35互換の述語で、それぞれobjが
&message
、&serious
、あるいは&error
型のコンディションであるか
どうかを検査します。
<error>
のサブクラス。
システムコールがエラーを返したとき、この型の例外が投げられます。
message
スロットには通常エラーの(strerror(3)
のような)説明が
含まれています。それ以外に、このクラスにはもうひとつ次のような
インスタンススロットがあります。
<system-error>
: errno ¶システムのエラー番号の整数値を持ちます。
エラー番号はシステムによって異なる可能性があります。Gaucheは典型的な
Unixのエラー番号に対して定数を定義している(例: EACCES
, EBADF
)ので、
それを使うと良いでしょう。定義されている定数に関しては
システムへの問い合わせのsys-strerror
の説明を参照してください。
このクラスには対応する SRFI のコンディションタイプがありませんが、
OSの生のエラーコードを取得するのに重要です。いくつかのケースで
このタイプのコンディションは他の、たとえば <io-read-error>
のようなコンディションと合成されます。
<error>
のサブクラス。多くのシグナルのデフォルトハンドラは
このコンディションを投げます。詳しくはシグナルの処理を
参照してください。
<unhandled-signal-error>
: signal ¶受け取ったシグナル番号を示す整数値。典型的なシグナル番号については 定数が定義されています。シグナルとシグナルセットを参照のこと。
[SRFI-36]
<error>
のサブクラス。リーダがS式を読み込み中に、字句エラー
または構文エラーを検出したとき、この型のコンディションが投げられます。
<read-error>
: port ¶リーダがS式を読みこんでいたポート。
(註: SRFI-36 の &read-error
はこのスロットを定義していません。
ポータブルなプログラムを書く場合はこのスロットを使わないで下さい)。
<read-error>
: line ¶リーダがこのエラーを発生させたときの入力行カウント(1がベース)。 リーダが行カウントを保持しないポートから読み込むときには、-1 となる。
[SRFI-36]
ポート関連の I/O エラー。<io-error>
を継承しています。
<port-error>
: port ¶エラーを起したポートを保持。
[SRFI-36]
ポートから読み込み中の I/O エラー。<port-error>
を継承しています。
[SRFI-36]
ポートへの書き出し中の I/O エラー。<port-error>
を継承しています。
[SRFI-36]
クローズされたポートで読み込み/書き出しをしようしたときの I/O エラー。
<port-error>
を継承しています。
対象となるポートでサポートされていない単位での読み/書き要求
(たとえば、キャラクタ専用ポートでのバイナリ I/O 要求)の際の I/O エラー。
<port-error>
を継承しています。
[SRFI-226] 継続に関する制約が破られた場合に投げられるエラーです。 典型的なのは、指定された継続プロンプトタグがコンテクスト中に見つからない場合です。 詳しくは継続プロンプトを参照してください。
[SRFI-226]
<continuation-violation>
コンディションを作ります。
prompt-tagは原因となった継続プロンプトタグです。
[SRFI-226]
objが<continuation-violation>
コンディションなら
#t
を、そうでなければ#f
を返します。
[SRFI-226]
<continuation-violation>
コンディションであるconditionの、
prompt-tagスロットの値を取り出します。
[SRFI-35+]
新しいコンディションタイプを定義します。Gaucheでは、コンディションタイプは
クラスであり、そのメタクラスは <condition-meta>
です。
name が新しいタイプの名前になり、この名前の変数が作成された
コンディションタイプに束縛されます。supertype はこのコンディション
タイプのスーパータイプ(直接のスーパークラス)の名前です。コンディション
タイプは <condition>
を継承するか、その子孫を継承しなければ
なりません。(この形式では、多重継承を指定することはできません。
一般的にいって、コンディションタイプの階層の中では多重継承は避ける
べきです。そのかわりに、合成コンディションを使えます。合成コンディションは
多重継承を使いません。)
変数 predicate はこのコンディションタイプ用の述語手続きに 束縛されます。
各 field-spec
は (field-name accessor-name)
の形式で
このコンディションは、フィールド名は field-name で決まります。
変数 accessor-name はそのフィールドにアクセスする手続きに
束縛されます。Gauche では、それぞれのフィールドは生成された
クラスのスロットとなります。
GaucheはSRFI-35を拡張して、predicateやaccessor-nameを
定義する必要が無い場合はその位置に#f
を指定できるようにしています。
accessor-nameが必要無い場合はそれを省略することもできます。
define-condition-type
がクラス定義に展開される際に、
各スロットは:init-keyword
スロットオプションにスロット名と
同名のキーワードを取るように定義されます。
[SRFI-35]
objがコンディションタイプである場合で、その場合にかぎり、
#t
を返します。Gauche では (is-a? obj <condition-meta>)
と同じです。
[SRFI-35] 新しいコンディションタイプを生成する手続き版です。
[SRFI-35]
コンディションタイプ type のコンディションを生成し、
field-name
および value
のペアで指定されたように
フィールドを初期化します。
[SRFI-35]
obj がコンディションである場合で、その場合にかぎり、
#t
を返します。Gauche では (is-a? obj <condition>)
と
同じです。
[SRFI-35]
obj がコンディションタイプ type に属している場合で、その
場合にかぎり、#t
を返します。合成コンディションがあるので、
これは、is-a?
と同じではありません
[SRFI-35]
condition のフィールド field-name の値を検索します。
condition が合成コンディションであれば、元のコンディションの
フィールドにアクセスできます。もし、複数の元のコンディションが、
field-name を持つ場合には、最初に make-compound-condition
に渡されたものが優先されます。
コンディションのフィールドにアクセスするには、slot-ref
および
ref
の両方あるいはどちらかを使えます。合成コンディションでは、
slot-missing
メソッドが定義されますので、slot-ref
は
あたかも、合成コンディションが元になったコンディションの全てのスロットを
もつかのように振舞います。しかしながら、condition-ref
を
使う方が可搬性が増します。
[SRFI-35+]
conditionが<message-condition>
を持っていれば
そのmessage
スロットを、そうでなければfallbackを返す手続きです。
fallbackが省略された場合は#f
が指定されたとみなします。
投げられたコンディションを一度捕まえてメッセージを記録したりユーザに見せたりする
一般的なコードはよくあります。Schemeでは任意のオブジェクトをraise
できるため、
捕まえたコンディションがメッセージスロットを持っているとは限りません。
従ってそういったコードはいちいちコンディションの型を検査して分岐することになります。
そのパターンは充分によくあるので、この手続きが追加されました。
[SRFI-35]
condition0 condition1 … のすべてを持つ合成コンディション
を返します。返されたコンディションのフィールドは、与えられたコンディション
のすべてのフィールドの和集合になります。同じ名前のフィールドを持つ
コンディションがある場合には最初に与えられものが優先されます。
返されたコンディションは元になったコンディションのすべてのタイプの
コンディションタイプをもつことになります。
(これは多重継承ではありません。上の <compound-condition>
を参照)
[SRFI-35] condition はコンディションで、condition-typeタイプで なければなりません。この手続きは condition-type のコンディション を返し、condition からとりだされた値のフィールドを持ちます。
[SRFI-35]
コンディションを生成するのに便利なマクロ。
合成されたコンディションも生成できます。
Type-field-binding は、
(condition-type (field-name value-expr) …)
という形式になります。
(condition (type0 (field00 value00) ...) (type1 (field10 value10) ...) ...) ≡ (make-compound-condition (make-condition type0 'field00 value00 ...) (make-condition type1 'field10 value10 ...) ...)