GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Module-Functions.html ・ emacs-doc.conao3.com

E.7.2 モジュール関数の記述

Emacsモジュールを作成する主な目的は、そのモジュールを読み込むLispプログラムで追加の関数を利用できるようにすることです。本サブセクションでは、こうしたモジュール関数の書き方について説明します。

モジュール関数は、以下のような一般的な形式とシグネチャを持ちます。

関数: emacs_value emacs_function (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data)

env 引数は、Emacs のオブジェクトや関数にアクセスするために必要な API 環境へのポインタを提供します。nargs 引数は必須の引数数であり、0 でも構いません(引数数のより柔軟な指定については、後述の make_function を参照してください)。また、args は関数引数の配列へのポインタです。data 引数は、関数が必要とする追加データを指しており、これは emacs_function から Emacs 関数を生成するために make_function(後述)が呼び出された際に用意されたものです。

モジュール関数は、Emacsとモジュール間でLispオブジェクトをやり取りするために型 emacs_value を使用します(Lispとモジュール値の変換を参照)。以下および後続のサブセクションで説明するAPIは、基本的なCデータ型と対応するemacs_valueオブジェクトとの間の変換機能を提供します。

モジュール関数の本体内では、インデックス nargs-1 を超える args 配列の要素にアクセスしようとしないでください。args 配列用のメモリは nargs 個の値を格納するのにちょうど十分な量だけ確保されており、それを超えてアクセスすると、ほとんどの場合モジュールがクラッシュします。特に、実行時に関数に渡される nargs の値が 0 である場合、args 用のメモリは確保されていないため、一切アクセスしてはいけません。

モジュール関数は常に値を返します。関数が正常に終了した場合、それを呼び出したLispコードには、関数が返したemacs_valueという値に対応するLispオブジェクトが表示されます。ただし、ユーザーがC-gと入力した場合や、モジュール関数またはその呼び出し先がエラーを通知した場合、あるいは非局所的に終了した場合(モジュールにおける非局所的な終了を参照)、Emacsは返された値を無視し、Lispコードが同様の状況に遭遇したときと同様に、終了または例外を発生させます。

ヘッダー emacs-module.h は、モジュール関数への関数ポインタのエイリアス型として、型 emacs_function を提供します。

モジュール関数のCコードを記述した後は、環境内で提供されているポインタを用いて、make_function関数によりそこからLisp関数オブジェクトを作成してください(環境へのポインタはget_environmentによって返されることを思い出してください)。通常、これはAPIの互換性を確認した上で、モジュール初期化関数(モジュール初期化関数を参照)内で行われます。

関数: emacs_value make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity, emacs_function func, const char *docstring, void *data)

これは、前述の emacs_function で説明したシグネチャを持つ C 関数 func から作成された Emacs 関数を返します。引数 min_arity と max_arity は、func が受け付け可能な引数の最小数と最大数を指定します。引数 max_arity には特殊値 emacs_variadic_function を指定でき、これにより Lisp の &rest キーワードと同様に、関数が無制限の数の引数を受け付けるようになります(引数リストの機能 を参照)。

引数 data は、func が呼び出される際に任意の追加データを渡せるようにするための仕組みです。make_function に渡されたポインタは、そのまま func に渡されます。

引数 docstring は、関数のドキュメント文字列を指定します。これは、ASCII 文字列、UTF-8 エンコードされた非 ASCII 文字列、または NULL ポインタのいずれかである必要があります。後者の場合、その関数にドキュメントは付与されません。ドキュメント文字列の末尾には、指定される呼び出し規約を記述する行を置くことができます。詳細は 関数のドキュメント文字列 を参照してください。

すべてのモジュール関数は第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 関数(モジュール初期化関数を参照)を含むモジュール関数は、Emacs から直接的または間接的に呼び出されている間、何らかのライブな emacs_env ポインタから環境関数を呼び出すことによってのみ Emacs とやり取りできます。言い換えれば、モジュール関数が Lisp 関数や Emacs プリミティブを呼び出したい場合、emacs_value オブジェクトを C のデータ型に変換したりその逆を行ったり(Lisp とモジュール値の変換を参照)、あるいはその他の方法で Emacs とやり取りしたい場合は、Emacs から emacs_module_init またはモジュール関数への呼び出しがコールスタック上に存在している必要があります。モジュール関数は、ガベージコレクションの実行中に Emacs とやり取りすることはできません。ガベージコレクションを参照してください。また、Emacs によって作成された Lisp インタプリタスレッド(メインスレッドを含む)からのみ Emacs とやり取りできます。スレッドを参照してください。--module-assertions コマンドラインオプションを使用すると、上記の要件に対する一部の違反を検出できます。The GNU Emacs Manual の 初期オプションを参照してください。

モジュールのAPIを使用すると、インライン関数やマクロなど、より複雑な関数型やデータ型を定義することが可能です。しかし、結果として生成されるCコードは煩雑で可読性が低くなります。そのため、関数やデータ構造を生成するモジュールコードは必要最小限に留め、残りはモジュールに付随するLispパッケージに任せることを推奨します。こうした追加処理をLispで行う方がはるかに容易であり、より可読性の高いコードが得られるためです。例えば、上述のように定義されたモジュール関数module-funcに基づき、マクロmodule-macroを作成する一つの方法として、以下のようなシンプルなLispラッパーが挙げられます。

(defmacro module-macro (&rest args)
  "Documentation string for the macro."
  (module-func args))

モジュールに対応するLispパッケージは、Emacsにロードされる際に、loadプリミティブ(Emacs Dynamic Modulesを参照)を用いてそのモジュールをロードできます。

デフォルトでは、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 によって返されるオブジェクト)がガベージコレクションされる際にコードを実行したい場合は、関数ファイナライザをインストールできます。関数ファイナライザは Emacs 28 から利用可能です。例えば、ヒープ上に確保された構造体を make_function の引数 data に渡した場合、ファイナライザを使用してその構造体を解放できます。(libc)Basic Allocation および (libc)Freeing after Malloc を参照してください。ファイナライザ関数のシグネチャは以下の通りです。

void finalizer (void *data)

ここでは、make_functionを呼び出す際にdataに渡された値が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つの関数につき設定できる関数ファイナライザは最大1つまでであり、arg に既にファイナライザが存在する場合は、それが fin に置き換えられます。