Next: , Previous: , Up: Errors   [Contents][Index]


11.7.3.3 エラーを処理するコードの記述

エラーをシグナルすることによる通常の効果は、実行されていたコマンドを終了してEmacsエディターのコマンドループに即座にリターンすることです。スペシャルフォームcondition-caseを使用してエラーハンドラーを設定することにより、プログラム内の一部で発生するエラーのをトラップを調整することができます。以下は単純な例です:

(condition-case nil
    (delete-file filename)
  (error nil))

これはfilenameという名前のファイルを削除して、任意のエラーをcatch、エラーが発生した場合はnilをリターンします(このような単純なケースではマクロignore-errorsを使用することもできる。以下を参照のこと)。

condition-case構文は、insert-file-contents呼び出しによるファイルオープンの失敗のような、予想できるエラーをトラップするために多用されます。condition-case構文はユーザーから読み取った式を評価するプログラムのような、完全には予測できないエラーのトラップにも使用されます。

condition-caseの2番目の引数は保護されたフォーム(protected form)と呼ばれます(上記の例では保護されたフォームはdelete-fileの呼び出し)。このフォームの実行が開始されるとエラーハンドラーが効果をもち、このフォームがリターンすると不活性になります。その間のすべてにおいてエラーハンドラーは効果をもちます。特にこのフォームで呼び出された関数とそのサブルーチン等を実行する間、エラーハンドラーは効果をもちます。厳密にいうと保護されたフォーム自身ではなく、保護されたフォームから呼び出されたLispプリミティブ関数(signalerrorを含む)だけがシグナルされるというのは、よいことと言えます。

保護されたフォームの後の引数はハンドラーです。各ハンドラーはそれぞれ、どのエラーを処理するかを指定する1つ以上のコンディション名(シンボル)をリストします。エラーがシグナルされたとき、エラーシンボルはコンディション名のリストも定義します。エラーが共通のコンディション名をもつ場合、そのハンドラーがそのエラーに適用されます。上記の例では1つのハンドラーがあり、それはすべてのエラーをカバーするコンディション名errorを指定しています。

適切なハンドラーの検索は、もっとも最近に設定されたハンドラーから始まり、設定されたすべてのハンドラーをチェックします。したがってネストされたcondition-caseフォームに同じエラー処理がある場合には、内側のハンドラーがそれを処理します。

何らかのcondition-caseによりエラーが処理されると、debug-on-errorでエラーによりデバッガーが呼び出されるようにしていても、通常はデバッガーの実行が抑制されます。

condition-caseで補足されるようなエラーをデバッグできるようにしたいなら、変数debug-on-signalに非nil値をセットします。以下のようにコンディション内にdebugを記述することにより、最初にデバッガーを実行するような特定のハンドラーを指定することもできます:

(condition-case nil
    (delete-file filename)
  ((debug error) nil))

ここでのdebugの効果は、デバッガー呼び出しを抑制するcondition-caseを防ぐことだけです。debug-on-errorとその他のフィルタリングメカニズムがデバッガーを呼び出すように指定されているときだけ、エラーによりデバッガーが呼び出されます。Error Debuggingを参照してください。

Macro: condition-case-unless-debug var protected-form handlers…

マクロcondition-case-unless-debugは、そのようなフォームのデバッギングを処理する、別の方法を提供する。このマクロは変数debug-on-errornil、つまり任意のエラーを処理しないようなケース以外は、condition-caseとまったく同様に振る舞う。

特定のハンドラーがそのエラーを処理するとEmacsが判断すると、Emacsは制御をそのハンドラーにreturnします。これを行うために、Emacsはそのとき脱出しつつあるバインディング構成により作成されたすべての変数のバインドを解き、そのとき脱出しつつあるすべてのunwind-protectフォームを実行します。制御がそのハンドラーに達すると、そのハンドラーのbodyが通常どおり実行されます。

そのハンドラーのbodyを実行した後、condition-caseフォームから実行がreturnされます。保護されたフォームは、そのハンドラーの実行の前に完全にexitしているので、そのハンドラーはそのエラーの位置から実行を再開することはできず、その保護されたフォーム内で作られた変数のバインディングを調べることもできません。ハンドラーが行なえることは、クリーンアップと、処理を進行させることだけです。

エラーのシグナルとハンドルにはthrowcatch (Catch and Throwを参照)に類似する点がいくつかありますが、これらは完全に別の機能です。エラーはcatchでキャッチできず、throwをエラーハンドラーで処理することはできません(しかし対応するcatchが存在しないときにthrowを使用することによりシグナルされるエラーは処理できる)。

Special Form: condition-case var protected-form handlers…

このスペシャルフォームはprotected-formの実行を囲い込むエラーハンドラーhandlersを確立する。エラーなしでprotected-formが実行されると、リターンされる値はcondition-caseフォームの値になる。この場合、condition-caseは効果をもたない。protected-formの間にエラーが発生すると、condition-caseフォームは違いを生じる。

handlersはそれぞれ、(conditions body…)というフォームのリストである。ここでconditionsはハンドルされるエラーコンディション名、またはそのハンドラーの前にデバッガーを実行するためのコンディション名(debugを含む)。bodyはこのハンドラーがエラーを処理するときに実行される1つ以上のLisp式。

(error nil)

(arith-error (message "Division by zero"))

((arith-error file-error)
 (message
  "Either division by zero or failure to open a file"))

発生するエラーはそれぞれ、それが何の種類のエラーかを記述するエラーシンボル(error symbol)をもち、これはコンディション名のリストも記述する(Error Symbolsを参照)。Emacsは1つ以上のコンディション名を指定するハンドラーにたいして、すべてのアクティブなcondition-caseフォームを検索する。condition-caseの最内のマッチがそのエラーを処理する。condition-case内では、最初に適合したハンドラーがそのエラーを処理する。

ハンドラーのbodyを実行した後、condition-caseは通常どおりリターンして、ハンドラーのbodyの最後の値をハンドラー全体の値として使用する。

引数varは変数である。protected-formを実行するとき、condition-caseはこの変数をバインドせず、エラーを処理するときだけバインドする。その場合には、varエラー記述(error description)にバインドする。これはエラーの詳細を与えるリストである。このエラー記述は(error-symbol . data)というフォームをもつ。ハンドラーは何を行なうか決定するために、このリストを参照することができる。たとえばファイルオープンの失敗にたいするエラーなら、ファイル名がdata(エラー記述の3番目の要素)の2番目の要素になる。

varnilなら、それはバインドされた変数がないことを意味する。この場合、エラーシンボルおよび関連するデータは、そのハンドラーでは利用できない。

より外側のレベルのハンドラーにcatchさせるために、condition-caseによりcatchされたシグナルを再度throwする必要がある場合もある。以下はこれを行なう方法である:

  (signal (car err) (cdr err))

ここでerrはエラー記述変数(error description variable)で、condition-caseの1番目の引数は、再throwしたいエラーコンディション。Definition of signalを参照のこと。

Function: error-message-string error-descriptor

この関数は与えられたエラー記述子(error descriptor)にたいするエラーメッセージ文字列をリターンする。これはそのエラーにたいする通常のエラーメッセージをプリントすることにより、エラーを処理したい場合に有用。Definition of signalを参照のこと。

以下は0除算の結果によるエラーを処理するために、condition-caseを使用する例です。このハンドラーは、(beepなしで)エラーメッセージを表示して、非常に大きい数をリターンします。

(defun safe-divide (dividend divisor)
  (condition-case err
      ;; 保護されたフォーム
      (/ dividend divisor)
    ;; ハンドラー
    (arith-error                        ; コンディション
     ;; このエラーにたいする、通常のメッセージを表示する
     (message "%s" (error-message-string err))
     1000000)))
⇒ safe-divide

(safe-divide 5 0)
     -| Arithmetic error: (arith-error)
⇒ 1000000

このハンドラーはコンディション名arith-errorを指定するので、division-by-zero(0除算)エラーだけを処理します。他の種類のエラーは(このcondition-caseによっては)、処理されません。したがって:

(safe-divide nil 3)
     error→ Wrong type argument: number-or-marker-p, nil

以下はerrorによるエラーを含む、すべての種類のエラーをcatchするcondition-caseです:

(setq baz 34)
     ⇒ 34

(condition-case err
    (if (eq baz 35)
        t
      ;; 関数errorの呼び出し
      (error "Rats!  The variable %s was %s, not 35" 'baz baz))
  ;; フォームではないハンドラー
  (error (princ (format "The error was: %s" err))
         2))
-| The error was: (error "Rats!  The variable baz was 34, not 35")
⇒ 2
Macro: ignore-errors body…

この構文は、それの実行中に発生する任意のエラーを無視してbodyを実行する。その実行中にエラーがなければ、ignore-errorsbody内の最後のフォームの値を、それ以外はnilをリターンする。

以下はこのセクションの最初の例をignore-errorsを使用して記述する例である:

  (ignore-errors
   (delete-file filename))
Macro: with-demoted-errors format body…

このマクロはいわばignore-errorsの穏やかなバージョンである。これはエラーを完全に抑止するのではなく、エラーをメッセージに変換する。これはメッセージのフォーマットに、文字列formatを使用する。format"Error: %S"のように、単一の‘%’シーケンスを含むこと。エラーをシグナルするとは予測されないが、もし発生した場合は堅牢であるべきようなコードの周囲にwith-demoted-errorsを使用する。このマクロはcondition-caseではなく、condition-case-unless-debugを使用することに注意。


Next: , Previous: , Up: Errors   [Contents][Index]