他のライブラリーの関数定義を変更する必要があるとき、およびfoo-function
oのようなフックやプロセスフィルター(process
filter)や、関数を値としてもつ任意の変数やオブジェクトを変更する必要があるときには、名前つきの関数にはfset
かdefun
、フック変数にはsetq
、プロセスフィルターにはset-process-filter
のように、適切なセッター関数(setter
function)を使用することができます。しかしこれらが以前の値を完全に破棄してしまうのが好ましくない場合もあります。
アドバイス(advice)機能によって関数にアドバイスすることにより、既存の関数定義に機能を追加できます。これは関数全体を再定義するより明解な手法です。
Emacsのアドバイスシステムは2つのプリミティブセットを提供します。コアとなるセットは変数やオブジェクトのフィールドに保持された関数値にたいするものです(対応するプリミティブはadd-function
とremove-function
)。もう1つのセットは名前つき関数の最上位のレイヤーとなるものです(主要なプリミティブはadvice-add
とadvice-remove
)。
簡単な例として、以下は関数の呼び出し時に毎回リターン値を変更するアドバイスを追加する方法です:
(defun my-double (x) (* x 2)) (defun my-increase (x) (+ x 1)) (advice-add 'my-double :filter-return #'my-increase)
このアドバイスの追加後に‘3’でmy-double
を呼び出すとリターン値は‘7’になるでしょう。このアドバイスを削除するには、以下のようにします
(advice-remove 'my-double #'my-increase)
より高度な例として、以下はプロセスprocのプロセスフィルターの呼び出しをトレースする例です:
(defun my-tracing-function (proc string) (message "Proc %S received %S" proc string)) (add-function :before (process-filter proc) #'my-tracing-function)
これによりそのプロセスの出力は元のプロセスフィルターに渡される前に、my-tracing-function
に渡されるようになります。my-tracing-function
は元の関数と同じ引数を受け取ります。これを行えば以下のようにしてトレースを行う前の振る舞いにリバートすることができます。
(remove-function (process-filter proc) #'my-tracing-function)
同様にdisplay-buffer
という名前つきの関数の実行をトレースしたいなら以下を使用できます:
(defun his-tracing-function (orig-fun &rest args) (message "display-buffer called with args %S" args) (let ((res (apply orig-fun args))) (message "display-buffer returned %S" res) res)) (advice-add 'display-buffer :around #'his-tracing-function)
ここでhis-tracing-function
は元の関数のかわりに呼び出されて、元の関数(に加えてその関数の引数)を引数として受け取るので、必要な場合はそれを呼び出すことができます。出力を確認し終えたら、以下のようにしてトレースを行う前の振る舞いにリバートできます:
(advice-remove 'display-buffer #'his-tracing-function)
上記の例で使用されている引数:before
と:around
は、2つの関数が構成される方法を指定します(これを行う多くの方法があるから)。追加された関数もアドバイス(advice)と呼ばれます。