Emacsモジュールを記述する主な理由は、そのモジュールをロードしたLispプログラムが追加の関数を利用できるようにするためです。このサブセクションでは、そのようなモジュール関数(module functions)の記述方法を説明します。
モジュール関数は以下のような一般的なフォームとシグネチャをもっています:
emacs_value
emacs_function (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data)
¶引数envはEmacsのオブジェクトや関数へのアクセスに必要となるAPI環境へのポインターを提供する。引数nargsは要求される引数の個数であり0もあり得る(引数の個数にたいするより柔軟な仕様については以下のmake_function
を参照)。argsは関数の引数へのポインター。引数dataは関数により要求される追加データへのポインターであり、module_func
からEmacs関数を作成するためにemacs_function
(以下参照)が呼び出される際にアレンジされる。
モジュール関数はEmacsとモジュール間でLispオブジェクトをやり取りするためにタイプemacs_value
を使用する(Lisp・モジュール間の値変換を参照)。以下で説明するAPIと以降のサブセクションではCの基本データ型と、それらに対応するemacs_value
オブジェクトの慣習にたいする機能を提供する。
モジュール関数のbodyにおいては、インデックスnargs-1
を超えた配列argsの要素へのアクセスを試みてはならない。配列argsにたいするメモリーはnargsの値を正確に収納できるように割り当てられるため、それを超えてアクセスを行うとあなたのモジュールはほとんどの場合はクラッシュするだろう。特に実行時に関数に渡されたnargsの値が0の場合には、メモリーは何も割り当てられないのでargsにアクセスしてはならない。
モジュール関数は常に値をリターンする。関数が正常にリターンすると、それに呼び出されたLispコードは関数がリターンしたemacs_value
値に対応するLispオブジェクトを目にすることになる。しかしユーザーがC-gをタイプしたり、モジュール関数やその呼び出し先がエラーをシグナルしたり非ローカルなexit
(モジュールでの非ローカル脱出を参照)を行なった場合には、Emacsはリターン値を無視してLispコードが同じ状況に遭遇した際のようにquitやthrrowを行う。
ヘッダーemacs-module.hは関数ポインターからモジュール関数へのエイリアス型としてemacs_function
型をを提供する。
モジュール関数用のCコード記述後には、そこからmake_function
を使用してLisp関数オブジェクトを作成する必要があります。make_function
関数へのポインターは環境内で提供されます(環境へのポインターはget_environment
がリターンすることを思い出してほしい)。これは通常はAPIのモジュール初期化関数(module initialization functionを参照)の内部で互換性検証後に行われます。
emacs_value
make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity, emacs_function func, const char *docstring, void *data)
¶これはC関数funcが作成した上記emacs_function
で説明したシグネチャをもつEmacs関数をリターンする。引数min_arityとmax_arityは、funcが受け付ける引数の最大個数と最小個数を指定する。引数max_arityは特別な値emacs_variadic_function
をもつことができる。これはLispの&rest
キーワードのように、関数の受け付ける引数の個数を無制限にする(引数リストの機能を参照)。
引数dataはfuncの呼び出し時に任意の追加データを渡すための手段である。make_function
に渡されたポインターが何であれ、それは変更されずにfuncに渡される。
引数docstringはその関数用のドキュメント文字列を指定する。これはASCII文字列かUTF-8にエンコードされた非ASCII文字列、またはNULL
ポインターのいずれかであること。後者の場合には関数がドキュメントをもたないことを意味する。ドキュメント文字列はadvertised-calling-conventionを指定する行で終端できる。関数のドキュメント文字列を参照のこと。
すべてのモジュール関数は1つ目の引数として環境へのポインターを受け取らなければならないので、make_function
は任意のモジュール関数から呼び出され得るが、モジュールが一度ロードされればすべてのモジュール関数がEmacsに既知となるように、通常はこれをモジュール初期化関数で行うことを望むだろう。
最後にLispコードが関数を名前で呼び出せるようにLisp関数をシンボルにバインドする必要があります。これを行うにはモジュールAPI関数intern
(internを参照)を使用します。この関数のポインターもモジュール関数がアクセス可能な環境内で提供されています。
上述のステップを組み合わせて、モジュール初期化関数の中で以下のようにC用にアレンジしたコードmodule_func
が、Lispからmodule-func
として呼び出し可能になります:
emacs_env *env = runtime->get_environment (runtime); 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データタイプ(Lisp・モジュール間の値変換を参照)の間の変換、あるいは別の方法によるEmacsとの対話を行いたい場合には、Emacsからのemacs_module_init
やモジュール関数への呼び出しのいくつかがコールスタック上になければなりません。モジュール関数はガーベージコレクションの間はEmacsと対話しないかもしれません。ガーベージコレクションを参照してください。モジュール関数はEmacsが作成したLispインタープリタースレッド(メインスレッドを含む)とだけ対話するかもしれません。スレッドを参照してください。コマンドラインオプション--module-assertionsは、上記の要件にたいする違反のいくつかを検知できます。Initial
Options in The GNU Emacs Manualを参照してください。
モジュールAPIの使用により、より複雑な関数やデータ型(インライン関数、マクロ等)の定義が可能になります。ただしCの結果コードは扱いにくく、可読性も低下します。したがって関数やデータ構造を作成するモジュールコードを可能なかぎり最小限に留めるとともに、残りの部分をモジュールに付属するLispパッケージに付託することを推奨します。なぜならこれらの追加タスクをLispで行うのはより用意であり、より可読性の高いコードが生成されるでしょう。たとえば上記のようにモジュール関数module-func
が定義されていれば、以下のようなシンプルなLispラッパーによってマクロmodule-macro
を作成するのも1つの方法です:
(defmacro module-macro (&rest args) "Documentation string for the macro." (module-func args))
モジュールに同梱されるLispパッケージは、Emacsにパッケージがロードされる際にload
プリミティブ(Emacsのダイナミックモジュールを参照)を使用してモジュールをロードできます。
デフォルトではmake_function
により作成したモジュール関数は非インタラクティブです。これらをインタラクティブにするために、以下の関数を使用できます。
void
make_interactive (emacs_env *env, emacs_value function, emacs_value spec)
¶このEmacs
28以降で利用可能になった関数はインタラクティブ仕様specを使用して、関数functionをインタラクティブにする。Emacsはspecをinteractive
フォームの引数のように解釈する。interactive
の使用とinteractive
にたいするコード文字を参照のこと。functionはmake_function
がリターンするEmacsモジュール関数であること。
モジュール関数のインタラクティブ仕様を取得するためのネイティブなモジュールサポートは存在しないことに注意。これを行うためにはinteractive-form
を使用する。interactive
の使用を参照のこと。一度make_interactive
を使用してモジュール関数をインタラクティブにした後に、非インタラクティブにすることはできない。
モジュール関数オブジェクト(例:
make_function
がリターンしたオブジェクト)がガーベージコレクトされた際に何らかのコードを実行したい場合には、関数ファイナライザー(function
finalizer)をインストールできます。関数ファイナライザーはEmacs
28以降で利用できます。たとえばmake_function
のdata引数にヒープ割り当てした構造体を渡した場合には、構造体の割り当て解放にファイナライザーを使用できます。(libc)Basic
Allocationと(libc)Freeing after
Mallocを参照してください。ファイナライザー関数は以下のsignatureをもちます:
void finalizer (void *data)
ここでdataはmake_function
呼び出し時にdataに渡された値です。ファイナライザーがEmacsと相互作用する手段は何もないことに注意してください。
make_function
呼びの直後には、新たに作成された関数はファイナライザーをもちません。ファイナライザーを望む場合には、ファイナライザーを追加するためにset_function_finalizer
を使用します。
void
emacs_finalizer (void *ptr)
¶ヘッダーemacs-module.hはEmacsファイナライザー関数にたいするエイリアスタイプとしてタイプemacs_finalizer
を提供する。
emacs_finalizer
get_function_finalizer (emacs_env *env, emacs_value arg)
¶このEmacs
28以降で利用可能になった関数は、argにより示されるモジュール関数に関連付けられた関数ファイナライザーをリターンする。argはモジュール関数、すなわちmake_function
がリターンするオブジェクトを参照しなければならない。その関数に関連付けられたファイナライザーがなければ、NULL
をリターンする。
void
set_function_finalizer (emacs_env *env, emacs_value arg, emacs_finalizer fin)
¶このEmacs
28以降で利用可能になった関数は、argで示されるモジュール関数に関連付けられる関数ファイナライザーにfinをセットする。argはモジュール関数、すなわちmake_function
がリターンするオブジェクトを参照しなければならない。finにはargの関数ファイナライザーをクリアーするNULL
、あるいはargにより示されるオブジェクトのガーベージコレクト時に呼び出される関数へのポインターを指定できる。関数ごとに最大で1つの関数ファイナライザーをセットできる。argがすでにファイナライザーを所有する場合にはfinで置き換えられる。