Previous: , Up: Nonlocal Exits   [Contents][Index]


10.5.4 Cleaning Up from Nonlocal Exits

unwind-protect構成は、データ構造を一時的に不整合な状態に置くときは、重要です。これはエラーやthrouのイベントにより、再びデータを整合された状態にすることができます(バッファー内容の変更だけに使用される、他のクリーンアップ構成は、アトミックな変更グループです。Atomic Changesを参照してください)。

Special Form: unwind-protect body-form cleanup-forms…

unwind-protectは、制御がbody-formを離れる場合に、cleanup-formsが評価されるという保証の下、なにが起こった可に関わらず、body-formを実行します。body-formは通常どおり完了するかもしれず、unwind-protectの外でthrowが実行されたり、エラーが発生するかもしれませんが、cleanup-formsは評価されます。

body-formが正常に終了した場合、unwind-protectcleanup-formsを評価した後で、body-formの値をreturnします。body-formが終了しなかった場合、unwind-protectは通常の意味における値は、returnしません。

unwind-protectにより保護されるのは、body-formだけです。cleanup-forms自体の任意のフォームが、(throwまたはエラーにより)非ローカルにexitした場合、unwind-protectは残りのフォームが評価されることを保証しませんcleanup-formsの中の1つが失敗することが問題となる場合は、そのフォームの周囲に他のunwind-protectを配して保護します。

現在アクティブなunwind-protectフォーム数と、ローカルの変数バインディング数の和は、max-specpdl-size(Local Variablesを参照してください)により制限されます。

たとえば、以下は一時的な使用のために不可視のバッファーを作成して、終了する前に確実にそのバッファーをkillする例です:

(let ((buffer (get-buffer-create " *temp*")))
  (with-current-buffer buffer
    (unwind-protect
        body-form
      (kill-buffer buffer))))

(kill-buffer (current-buffer))のように記述して、変数bufferを使用せずに、同様のことを行えると思うかもしれません。しかし上の例は、別のバッファーにスイッチしたときにbody-formでエラーが発生した場合、より安全なのです(一時的なバッファーをkillするとき、そのバッファーがカレントとなることを確実にするために、かわりにbody-formの周囲にsave-current-bufferを記述することもできます)。

Emacsには、上のコードとおおよそ等しいコードに展開される、with-temp-bufferという標準マクロが含まれます(Current Bufferを参照してください)。このマニュアル中で定義されるいくつかのマクロは、この方法でunwind-protectを使用します。

以下は、FTPパッケージ由来の、実際の例です。これは、リモートマシンへの接続の確立を試みるために、プロセス(Processesを参照してください)を作成します。関数ftp-loginは、関数のライター(writer)が予想できないことによる多くの問題から非常に影響を受けるので、失敗イベントでプロセスの削除を保証するフォームで保護されています。そうしないと、Emacsは無用なサブプロセスで一杯になってしまうでしょう。

(let ((win nil))
  (unwind-protect
      (progn
        (setq process (ftp-setup-buffer host file))
        (if (setq win (ftp-login process host user password))
            (message "Logged in")
          (error "Ftp login failed")))
    (or win (and process (delete-process process)))))

この例には小さなバグがあります。ユーザーがquitするためにC-gとタイプした場合、関数ftp-setup-bufferがreturnした後、即座にquitが発生しますが、それは変数processがセットされる前なので、そのプロセスはkillされないでしょう。このバグを簡単に訂正する方法はありませんが、少なくともこれは非常に稀なことだと言えます。