Next: Module Misc, Previous: Module Functions, Up: Writing Dynamic Modules [Contents][Index]
非常に少数の例外を除くほとんどのモジュールでは、モジュールを呼び出すLispプログラムとの間でモジュール関数への引数やリターン値の受け渡しでデータのやり取りが必要になります。この目的にたいしてモジュールAPIはemacs_value
タイプを提供しています。これはAPIを通じたやり取りにおいてEmacsのLispオブジェクトを表現するタイプであり、EmacsのCプリミティブ(Writing Emacs Primitivesを参照)で使用されるLisp_Object
タイプと機能的には等価です。このセクションではLispの基本データ型に対応するemacs_value
オブジェクトの作成を可能とするモジュールAPIの部分と、Lispオブジェクトに対応するemacs_value
オブジェクト内のCデータへのアクセス方法について説明します。
以下で説明するすべての関数は、実際にはすべてのモジュール関数が受け取る環境へのポインターを介して提供される関数ポインター(function pointers)です。したがってモジュールのコードでは以下のように環境ポインターを通じてこれらの関数を呼び出す必要があります:
emacs_env *env; /* the environment pointer */ env->some_function (arguments…);
emacs_env
ポインターは通常はモジュール関数の1つ目の引数、モジュール初期化関数内で環境が必要な場合にはget_environment
の呼び出しから取得できます。
以下で説明するもののほとんどはEmacs 25で利用可能になった関数であり、Emacs 25はダイナミックモジュールを最初にサポートした最初のEmacsリリースです。それ以降のリリースで利用可能になったいくつかの関数につていは、それらをサポートする最初のEmacsバージョンを付記します。
以下のAPI関数はemacs_value
オブジェクトから種々のCデータ型を抽出します。これらすべては引数のemacs_value
オブジェクトがその関数の期待するタイプでなければ、エラーコンディションwrong-type-argument
をraiseします(Type Predicatesを参照)。Emacsモジュール内でエラーをシグナルする方法、およびEmacsにエラーが報告される前にモジュール内部でエラーコンディションをcatchする方法の詳細はModule Nonlocalを参照してください。emacs_value
のタイプ取得にはAPI関数type_of
を使用できます(type_ofを参照)。
この関数はargで指定されたLisp整数の値をリターンする。リターン値のCデータ型intmax_t
はCコンパイラーがサポートする最大の整数型であり、一般的にはlong long
。argの値がintmax_t
に収まらなければ、関数はエラーシンボルoverflow-error
を使用してエラーをシグナルする。
このEmacs
27から利用可能になった関数は、argの整数値を抽出する。argの値は整数(fixnumかbignum)でなければならない。signがNULL
以外なら、argの符号(-1、0、+1)を*sign
に格納する。マグニチュード(magnitude:
大きさ)は次のようにmagnitudeに格納される。countとmagnitudeがいずれも非NULL
なら、magnitudeは少なくとも*count
unsigned
long
要素の配列を指さなければならない。magnitudeがargのマグニチュードを保持するのに十分大きければ、この関数はmagnitude配列にリトルエンディアン形式でマグニチュードを書き込み、配列の要素数を*count
に格納してtrue
をリターンする。magnitudeの大きさが十分でなければ、必要な配列サイズを*count
に格納,エラーをシグナルしてfalse
をリターンする。countが非NULL
かつmagnitudeがNULL
なら、必要となる配列サイズを*count
に格納してtrue
をリターンする。
Emacsは*count
に要求される最大値がmin (PTRDIFF_MAX, SIZE_MAX) / sizeof
(emacs_limb_t)
を超えないことを保証するので、magnitude
配列の割り当てではサイズ計算で整数のオーバーフローを心配せずにmalloc
(*count * sizeof *magnitude)
を使用できる。
これは大きい整数向け変換関数のマグニチュード配列の要素タイプとして使用される符号なし整数タイプ。このタイプは一意なオブジェクト表現をもつ(パディングビットがない)ことが保証されている。
これはemacs_limb_t
にたいして可能な最大値を指定する定数式に展開されるマクロ。この式は#if
内での利用に適する。
この関数はargで指定されたLisp浮動小数の値をCのdouble
値としてリターンする。
このEmacs 27から利用可能になった関数はtimeをEmacs
Lispのtime値として解釈して、それに対応するstruct timespec
をリターンする。Time of Dayを参照のこと。struct timespec
はナノ秒の精度のタイムスタンプを表す。以下のメンバーをもつ:
time_t tv_sec
整数秒。
long tv_nsec
ナノ秒としての少数秒数。extract_time
がリターンするタイムスタンプでは常に非負かつ十億未満(tv_nsec
のタイプがlong
であることをPOSIXがが要求しているとしても、非標準的なプラットフォームではlong
long
である)。
(libc)Elapsed Timeを参照のこと。
timeがナノ秒より高い精度をもつ場合には、この関数はナノ秒の精度へ負の無限大方向に切り詰める。struct
timespec
が(ナノ秒に切り詰めた)timeを表現できなければ、この関数はエラーをシグナルする。たとえばtime_t
が32ビット整数タイプなら100億秒というtime値はエラーをシグナルするが、600ピコ秒のtime値は0に切り詰められるだろう。
struct
timespec
で表現できないtime値を処理する必要があったり、より高い精度が必要ならLisp関数encode-time
を呼び出してリターン値を処理すればよい。Time Conversionを参照のこと。
これはargで指定されたLisp文字列をUTF-8にエンコードしたテキストをbufが指すchar
配列に格納する。bufは少なくとも終端のnullバイトを含む*len
バイトを保持するために十分なスペースをもつこと。引数lenはNULL
ポインターであってはならない。この関数の呼び出し時にはbufのバイトサイズを指定する値を指していること。
*len
で指定されたバッファーサイズが文字列のテキストを保持するために十分大きければ、関数は終端のnullバイト含む実際にコピーされる*len
バイトをbufにコピーしてtrue
をリターンする。バッファーが小さすぎる場合には、関数はエラーコンディションargs-out-of-range
をraiseするとともに、必要なバイト数を*len
に格納してfalse
をリターンする。保留中のエラーコンディションのハンドル方法はModule Nonlocalを参照のこと。
引数bufはNULL
ポインターでもよく、この場合には関数はargのコンテンツの格納に必要なバイト数を*len
に格納してtrue
をリターンする。これは特定の文字列を格納するために必要なbufサイズを決定する手段となり得る。1回目はbufをNULL
でcopy_string_contents
を呼び出して、関数により*len
に格納されたバイト数の保持に十分なメモリーを割り当ててから、実際にテキストのコピーを行うために非NULL
のbufで関数を再び呼び出す。
この関数はvectorのindexの要素をリターンする。ベクターの最初の要素のindexは0。indexの値が無効ならこの関数はエラーコンディションargs-out-of-range
をraiseする。関数のリターン値からCデータを抽出するためには、ベクターの当該要素に格納されたLispデータタイプに応じて、ここで説明している他の抽出関数を使用すればよい。
この関数はvector内の要素数をリターンする。
この関数はvectorのインデックスindexの要素にvalueを格納する。indexの値が無効ならこの関数はエラーコンディションargs-out-of-range
をraiseする。
以下はCの基本データ型からemacs_value
オブジェクトを作成するAPI関数です。これらはすべて作成したemacs_value
オブジェクトをリターンします。
この関数は引数n
(整数)を受け取り対応するemacs_value
オブジェクトをリターンする。nの値がmost-negative-fixnum
とmost-positive-fixnum
の内外いずれであるかに応じてfixnumかbignumのいずれかをリターンする(Integer Basicsを参照)。nの値をEmacs整数で表現できない、すなわちmost-negative-fixnum
とmost-positive-fixnum
の範囲外(Integer Basicsを参照)ならエラーコンディションoverflow-error
をraiseする。
このEmacs
27から利用可能になった関数は任意のサイズの整数の引数を受け取り、それに対応するemacs_value
オブジェクトをリターンする。sign引数はリターン値の符号を与える。signが0以外なら、magnitudeはリターン値のマグニチュードをリトルエンディアンで指定する、少なくともcount個の要素数の配列を指さなければならない。
以下は与えられた整数の次の確率的素数を計算するためにGNU Multiprecision Library
(GMP)を使用する例です。GMPの概要は(gmp)Top、magnitude
とGMPのmpz_t
値との間の変換方法については(gmp)Integer
Import and Exportを参照してください。
#include <emacs-module.h> int plugin_is_GPL_compatible; #include <assert.h> #include <limits.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <gmp.h> static void memory_full (emacs_env *env) { static const char message[] = "Memory exhausted"; emacs_value data = env->make_string (env, message, strlen (message)); env->non_local_exit_signal (env, env->intern (env, "error"), env->funcall (env, env->intern (env, "list"), 1, &data)); } enum { order = -1, endian = 0, nails = 0, limb_size = sizeof (emacs_limb_t), max_nlimbs = ((SIZE_MAX < PTRDIFF_MAX ? SIZE_MAX : PTRDIFF_MAX) / limb_size) }; static bool extract_big_integer (emacs_env *env, emacs_value arg, mpz_t result) { ptrdiff_t nlimbs; bool ok = env->extract_big_integer (env, arg, NULL, &nlimbs, NULL); if (!ok) return false; assert (0 < nlimbs && nlimbs <= max_nlimbs); emacs_limb_t *magnitude = malloc (nlimbs * limb_size); if (magnitude == NULL) { memory_full (env); return false; } int sign; ok = env->extract_big_integer (env, arg, &sign, &nlimbs, magnitude); assert (ok); mpz_import (result, nlimbs, order, limb_size, endian, nails, magnitude); free (magnitude); if (sign < 0) mpz_neg (result, result); return true; } static emacs_value make_big_integer (emacs_env *env, const mpz_t value) { size_t nbits = mpz_sizeinbase (value, 2); int bitsperlimb = CHAR_BIT * limb_size - nails; size_t nlimbs = nbits / bitsperlimb + (nbits % bitsperlimb != 0); emacs_limb_t *magnitude = nlimbs <= max_nlimbs ? malloc (nlimbs * limb_size) : NULL; if (magnitude == NULL) { memory_full (env); return NULL; } size_t written; mpz_export (magnitude, &written, order, limb_size, endian, nails, value); assert (written == nlimbs); assert (nlimbs <= PTRDIFF_MAX); emacs_value result = env->make_big_integer (env, mpz_sgn (value), nlimbs, magnitude); free (magnitude); return result; } static emacs_value next_prime (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) { assert (nargs == 1); mpz_t p; mpz_init (p); extract_big_integer (env, args[0], p); mpz_nextprime (p, p); emacs_value result = make_big_integer (env, p); mpz_clear (p); return result; } int emacs_module_init (struct emacs_runtime *ert) { emacs_env *env = ert->get_environment (ert); emacs_value symbol = env->intern (env, "next-prime"); emacs_value func = env->make_function (env, 1, 1, next_prime, NULL, NULL); emacs_value args[] = {symbol, func}; env->funcall (env, env->intern (env, "defalias"), 2, args); return 0; }
この関数はdouble
の引数dを受け取り対応するEmacs浮動小数点値をリターンする。
このEmacs 27から利用可能になった関数はstruct
timespec
の引数timeを受け取り、(ticks
.
hz)
というペアーとしてそれに対応するEmacsタイムスタンプをリターンする。リターン値は正確にtimeと同一のタイムスタンプを表す。つまりすべての入力値は表現可能であり、精度を失うことは決してない。time.tv_sec
およびtime.tv_nsec
は任意の値をとり得る。特にtimeが正規化されている必要はない。これはtime.tv_nsec
が負、あるいは999,999,999より大きくなり得ることを意味する。
この関数はstrが指す、終端のnullバイトを含まないバイト長がstrlenであるようなCテキスト文字列からEmacs文字列を作成する。strの元文字列はASCII文字列かUTF-8にエンコードされた非ASCII文字列が可能であり、文字列には埋め込みのnullバイトを含むことができ、str[strlen]
にあるnullバイトで終端される必要はない。strlenが負、またはEmacs文字列の最大長を超過する場合には、この関数はエラーコンディションoverflow-error
をraiseする。
このAPIはたとえばcons
とlist
によるリスト作成(Building Listsを参照)、car
とcdr
によるリストメンバーの抽出(List Elementsを参照)、vector
によるベクター作成(Vector Functionsを参照)等のようなLispデータ構造を操作する関数は提供しません。これらにたいしてはたいおう
するLisp関数を呼び出すために、次のサブセクションで説明するintern
とfuncall
を使用します。
emacs_value
オブジェクトのライフタイムはかなり短いのが普通です。このライフタイムはオブジェクトの作成に使用されたemacs_env
ポインターがスコープ外になると終了します。emacs_value
が望む間は行き続けるようなグローバル参照(global
references)を作成を要する場合もあるかもしれません。そのようなオブジェクトの管理には以下の2つの関数を使用します。
この関数はvalueのグローバル参照をリターンする。
この関数は以前にmake_global_ref
で作成したglobal_valueを解放する。global_valueはこの呼び出し後は無効になる。モジュールのコードではmake_global_ref
と対応するfree_global_ref
の呼び出しそれぞれをペアーとすること。
後でモジュール関数に渡す必要があるCデータ構造体を追跡するための代替え手段はユーザーポインター(user
pointer)オブジェクトの作成です。ユーザーポインター(またはuser-ptr
)はCポインターをカプセル化したLispオブジェクトであり、関連付けられたファイナライザー(オブジェクトがガーベージコレクトされる際に呼び出される。Garbage Collectionを参照)をもつことができます。モジュールAPIはuser-ptr
オブジェクトの作成やアクセスを行う関数を提供します。これらの関数はuser-ptr
オブジェクトを表現しないemacs_value
で呼び出されるとエラーコンディションwrong-type-argument
をraiseします。
この関数はCポインターptrをラップしたuser-ptr
オブジェクトを作成してリターンする。ファイナライザー関数finはNULL
(ファイナライザーなし)、または以下のシグネチャをもつ関数のいずれか:
typedef void (*emacs_finalizer) (void *ptr);
finがNULL
ポインターでなければ、user-ptr
オブジェクトがガーベージコレクトされる際にptrを引数として呼び出される。Emacsの応答性を維持するためにGCは短時間で終了しなければならないので、ファイナライザーでは高価なコードの実行は行ってはならない。
この関数はvalで表されるLispオブジェクトからCポインターを抽出する。
この関数はvalで表されるuser-ptr
オブジェクトに埋め込まれたCポインターにptrをセットする。
この関数はvalで表されるuser-ptr
オブジェクトのファイナライザー、ファイナライザーがなければNULL
をリターンする。
この関数はvalで表されるuser-ptr
オブジェクトのファイナライザーをfinに変更する。finがNULL
ならuser-ptr
オブジェクトのファイナライザーはなくなる。
Next: Module Misc, Previous: Module Functions, Up: Writing Dynamic Modules [Contents][Index]