Previous: Errors, Up: Nonlocal Exits [Contents][Index]
unwind-protect
構成は、データ構造を一時的に不整合な状態に置くときは、重要です。これはエラーやthrouのイベントにより、再びデータを整合された状態にすることができます(バッファー内容の変更だけに使用される、他のクリーンアップ構成は、アトミックな変更グループです。Atomic Changesを参照してください)。
unwind-protect
は、制御がbody-formを離れる場合に、cleanup-formsが評価されるという保証の下、なにが起こった可に関わらず、body-formを実行します。body-formは通常どおり完了するかもしれず、unwind-protect
の外でthrow
が実行されたり、エラーが発生するかもしれませんが、cleanup-formsは評価されます。
body-formが正常に終了した場合、unwind-protect
はcleanup-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されないでしょう。このバグを簡単に訂正する方法はありませんが、少なくともこれは非常に稀なことだと言えます。