Next: Module Values, Previous: Module Initialization, Up: Writing Dynamic Modules [Contents][Index]
Emacsモジュールを記述する主な理由は、そのモジュールをロードしたLispプログラムが追加の関数を利用できるようにするためです。このサブセクションでは、そのようなモジュール関数(module functions)の記述方法を説明します。
モジュール関数は以下のような一般的なフォームとシグネチャをもっています:
引数envはEmacsのオブジェクトや関数へのアクセスに必要となるAPI環境へのポインターを提供する。引数nargsは要求される引数の個数であり0もあり得る(引数の個数にたいするより柔軟な仕様については以下のmake_function
を参照)。argsは関数の引数へのポインター。引数dataは関数により要求される追加データへのポインターであり、module_func
からEmacs関数を作成するためにmodule_func
(以下参照)が呼び出される際にアレンジされる。
モジュール関数はEmacsとモジュール間でLispオブジェクトをやり取りするためにタイプemacs_value
を使用する(Module Valuesを参照)。以下で説明するAPIと以降のサブセクションではCの基本データ型と、それらに対応するemacs_value
オブジェクトの慣習にたいする機能を提供する。
モジュール関数は常に値をリターンする。関数が正常にリターンすると、それに呼び出されたLispコードは関数がリターンしたemacs_value
値に対応するLispオブジェクトを目にすることになる。しかしユーザーがC-gをタイプしたり、モジュール関数やその呼び出し先がエラーをシグナルしたり非ローカルなexit
(Module Nonlocalを参照)を行なった場合には、Emacsはリターン値を無視してLispコードが同じ状況に遭遇した際のようにquitやthrrowを行う。
モジュール関数用のCコード記述後には、そこからmake_function
を使用してLisp関数オブジェクトを作成する必要があります。make_function
関数へのポインターは環境内で提供されます(環境へのポインターはget_environment
がリターンすることを思い出してほしい)。これは通常はAPIのモジュール初期化関数(module initialization functionを参照)の内部で互換性検証後に行われます。
これはC関数funcが作成した上記module_func
で説明したシグネチャをもつEmacs関数をリターンする(ここではsubr
にtypedef
されると仮定)。引数min_arityとmax_arityは、funcが受け付ける引数の最大個数と最小個数を指定する。引数max_arityは特別な値emacs_variadic_function
をもつことができる。これはLispの&rest
キーワードのように、関数の受け付ける引数の個数を無制限にする(Argument Listを参照)。
引数dataはfuncの呼び出し時に任意の追加データを渡すための手段である。make_function
に渡されたポインターが何であれ、それは変更されずにfuncに渡される。
引数docstringはその関数用のドキュメント文字列を指定する。これはASCII文字列かUTF-8にエンコードされた非ASCII文字列、またはNULL
ポインターのいずれかであること。後者の場合には関数がドキュメントをもたないことを意味する。ドキュメント文字列はadvertised-calling-conventionを指定する行で終端できる。Function Documentationを参照のこと。
すべてのモジュール関数は1つ目の引数として環境へのポインターを受け取らなければならないので、make_function
は任意のモジュール関数から呼び出され得るが、モジュールが一度ロードされればすべてのモジュール関数がEmacsに既知となるように、通常はこれをモジュール初期化関数で行うことを望むだろう。
最後にLispコードが関数を名前で呼び出せるようにLisp関数をシンボルにバインドする必要があります。これを行うにはモジュールAPI関数intern
(internを参照)を使用します。この関数のポインターもモジュール関数がアクセス可能な環境内で提供されています。
上述のステップを組み合わせて、モジュール初期化関数の中で以下のようにC用にアレンジしたコードmodule_func
が、Lispからmodule-func
として呼び出し可能になります:
emacs_env *env = ert->get_environment (ert); emacs_value func = env->make_function (env, min_arity, max_arity, module_func, docstring, data); emacs_value symbol = env->intern (env, "module-func"); emacs_value args[] = {symbol, func}; env->funcall (env, env->intern (env, "defalias"), 2, args);
env->intern
の呼び出しによりシンボルmodule-func
はEmacsが知ることとなり、それから関数をこのシンボルにバインドするためにEmacsからdefalias
を呼び出します。defalias
のかわりにfset
の使用も可能なことに注意してください。両者の違いはdefaliasに説明があります。
emacs_module_init
関数(module initialization functionを参照)を含むモジュール関数は、直接間接を問わずEmacsから呼び出されていれば、何らかの生きたemacs_env
ポインターによる環境関数呼び出しにより、Emacsだけと対話する可能性があります。言い換えると、モジュール関数によるLisp関数やEmacsプリミティブを呼び出し、emacs_value
とCデータタイプ(Module Valuesを参照)の間の変換、あるいは別の方法によるEmacsとの対話を行いたい場合には、Emacsからのemacs_module_init
やモジュール関数への呼び出しのいくつかがコールスタック上になければなりません。モジュール関数はガーベージコレクションの間はEmacsと対話しないかもしれません。Garbage Collectionを参照してください。モジュール関数はEmacsが作成したLispインタープリタースレッド(メインスレッドを含む)とだけ対話するかもしれません。Threadsを参照してください。コマンドラインオプション--module-assertionsは、上記の要件にたいする違反のいくつかを検知できます。Initial
Options in The GNU Emacs Manualを参照してください。
モジュールAPIの使用により、より複雑な関数やデータ型(インタラクティブ関数、インライン関数、マクロ等)の定義が可能になります。ただしCの結果コードは扱いにくく、可読性も低下します。したがって関数やデータ構造を作成するモジュールコードを可能なかぎり最小限に留めるとともに、残りの部分をモジュールに付属するLispパッケージに付託することを推奨します。なぜならこれらの追加タスクをLispで行うのはより用意であり、より可読性の高いコードが生成されるでしょう。たとえば上記のように定義されたモジュール関数module-func
がある場合には、以下のようなシンプルなLispラッパーにもとづくインタラクティブコマンドmodule-macro
を作成するのも1つの方法です:
(defun module-cmd (&rest args) "Documentation string for the command." (interactive spec) (apply 'module-func args))
モジュールに同梱されるLispパッケージは、Emacsにパッケージがロードされる際にload
プリミティブ(Dynamic Modulesを参照)を使用してモジュールをロードできます。