Next: , Previous: , Up: Writing Dynamic Modules   [Contents][Index]


E.8.3 Lisp・モジュール間の値変換

非常に少数の例外を除くほとんどのモジュールでは、モジュールを呼び出すLispプログラムとの間でモジュール関数への引数やリターン値の受け渡しでデータのやり取りが必要になります。この目的にたいしてモジュールAPIemacs_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を参照)。

Function: intmax_t extract_integer (emacs_env *env, emacs_value arg)

この関数はargで指定されたLisp整数の値をリターンする。リターン値のCデータ型intmax_tはCコンパイラーがサポートする最大の整数型であり、一般的にはlong longargの値がintmax_tに収まらなければ、関数はエラーシンボルoverflow-errorを使用してエラーをシグナルする。

Function: bool extract_big_integer (emacs_env *env, emacs_value arg, int *sign, ptrdiff_t *count, emacs_limb_t *magnitude)

このEmacs 27から利用可能になった関数は、argの整数値を抽出する。argの値は整数(fixnumかbignum)でなければならない。signNULL以外なら、argの符号(-1、0、+1)を*signに格納する。マグニチュード(magnitude: 大きさ)は次のようにmagnitudeに格納される。countmagnitudeがいずれも非NULLなら、magnitudeは少なくとも*count unsigned long要素の配列を指さなければならない。magnitudeargのマグニチュードを保持するのに十分大きければ、この関数はmagnitude配列にリトルエンディアン形式でマグニチュードを書き込み、配列の要素数を*countに格納してtrueをリターンする。magnitudeの大きさが十分でなければ、必要な配列サイズを*countに格納,エラーをシグナルしてfalseをリターンする。countが非NULLかつmagnitudeNULLなら、必要となる配列サイズを*countに格納してtrueをリターンする。

Emacsは*countに要求される最大値がmin (PTRDIFF_MAX, SIZE_MAX) / sizeof (emacs_limb_t)を超えないことを保証するので、magnitude配列の割り当てではサイズ計算で整数のオーバーフローを心配せずにmalloc (*count * sizeof *magnitude)を使用できる。

Type alias: emacs_limb_t

これは大きい整数向け変換関数のマグニチュード配列の要素タイプとして使用される符号なし整数タイプ。このタイプは一意なオブジェクト表現をもつ(パディングビットがない)ことが保証されている。

Macro: EMACS_LIMB_MAX

これはemacs_limb_tにたいして可能な最大値を指定する定数式に展開されるマクロ。この式は#if内での利用に適する。

Function: double extract_float (emacs_env *env, emacs_value arg)

この関数はargで指定されたLisp浮動小数の値をCのdouble値としてリターンする。

Function: struct timespec extract_time (emacs_env *env, emacs_value time)

この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を参照のこと。

Function: bool copy_string_contents (emacs_env *env, emacs_value arg, char *buf, ptrdiff_t *len)

これはargで指定されたLisp文字列をUTF-8にエンコードしたテキストをbufが指すchar配列に格納する。bufは少なくとも終端のnullバイトを含む*lenバイトを保持するために十分なスペースをもつこと。引数lenNULLポインターであってはならない。この関数の呼び出し時にはbufのバイトサイズを指定する値を指していること。

*lenで指定されたバッファーサイズが文字列のテキストを保持するために十分大きければ、関数は終端のnullバイト含む実際にコピーされる*lenバイトをbufにコピーしてtrueをリターンする。バッファーが小さすぎる場合には、関数はエラーコンディションargs-out-of-rangeをraiseするとともに、必要なバイト数を*lenに格納してfalseをリターンする。保留中のエラーコンディションのハンドル方法はModule Nonlocalを参照のこと。

引数bufNULLポインターでもよく、この場合には関数はargのコンテンツの格納に必要なバイト数を*lenに格納してtrueをリターンする。これは特定の文字列を格納するために必要なbufサイズを決定する手段となり得る。1回目はbufNULLcopy_string_contentsを呼び出して、関数により*lenに格納されたバイト数の保持に十分なメモリーを割り当ててから、実際にテキストのコピーを行うために非NULLbufで関数を再び呼び出す。

Function: emacs_value vec_get (emacs_env *env, emacs_value vector, ptrdiff_t index)

この関数はvectorindexの要素をリターンする。ベクターの最初の要素のindexは0。indexの値が無効ならこの関数はエラーコンディションargs-out-of-rangeをraiseする。関数のリターン値からCデータを抽出するためには、ベクターの当該要素に格納されたLispデータタイプに応じて、ここで説明している他の抽出関数を使用すればよい。

Function: ptrdiff_t vec_size (emacs_env *env, emacs_value vector)

この関数はvector内の要素数をリターンする。

Function: void vec_set (emacs_env *env, emacs_value vector, ptrdiff_t index, emacs_value value)

この関数はvectorのインデックスindexの要素にvalueを格納する。indexの値が無効ならこの関数はエラーコンディションargs-out-of-rangeをraiseする。

以下はCの基本データ型からemacs_valueオブジェクトを作成するAPI関数です。これらはすべて作成したemacs_valueオブジェクトをリターンします。

Function: emacs_value make_integer (emacs_env *env, intmax_t n)

この関数は引数n (整数)を受け取り対応するemacs_valueオブジェクトをリターンする。nの値がmost-negative-fixnummost-positive-fixnumの内外いずれであるかに応じてfixnumかbignumのいずれかをリターンする(Integer Basicsを参照)。nの値をEmacs整数で表現できない、すなわちmost-negative-fixnummost-positive-fixnumの範囲外(Integer Basicsを参照)ならエラーコンディションoverflow-errorをraiseする。

Function: emacs_value make_big_integer (emacs_env *env, int sign, ptrdiff_t count, const emacs_limb_t *magnitude)

このEmacs 27から利用可能になった関数は任意のサイズの整数の引数を受け取り、それに対応するemacs_valueオブジェクトをリターンする。sign引数はリターン値の符号を与える。signが0以外なら、magnitudeはリターン値のマグニチュードをリトルエンディアンで指定する、少なくともcount個の要素数の配列を指さなければならない。

以下は与えられた整数の次の確率的素数を計算するためにGNU Multiprecision Library (GMP)を使用する例です。GMPの概要は(gmp)Topmagnitudeと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;
}
Function: emacs_value make_float (emacs_env *env, double d)

この関数はdoubleの引数dを受け取り対応するEmacs浮動小数点値をリターンする。

Function: emacs_value make_time (emacs_env *env, struct timespec time)

このEmacs 27から利用可能になった関数はstruct timespecの引数timeを受け取り、(ticks . hz)というペアーとしてそれに対応するEmacsタイムスタンプをリターンする。リターン値は正確にtimeと同一のタイムスタンプを表す。つまりすべての入力値は表現可能であり、精度を失うことは決してない。time.tv_secおよびtime.tv_nsecは任意の値をとり得る。特にtimeが正規化されている必要はない。これはtime.tv_nsecが負、あるいは999,999,999より大きくなり得ることを意味する。

Function: emacs_value make_string (emacs_env *env, const char *str, ptrdiff_t strlen)

この関数はstrが指す、終端のnullバイトを含まないバイト長がstrlenであるようなCテキスト文字列からEmacs文字列を作成する。strの元文字列はASCII文字列かUTF-8にエンコードされた非ASCII文字列が可能であり、文字列には埋め込みのnullバイトを含むことができ、str[strlen]にあるnullバイトで終端される必要はない。strlenが負、またはEmacs文字列の最大長を超過する場合には、この関数はエラーコンディションoverflow-errorをraiseする。

このAPIはたとえばconslistによるリスト作成(Building Listsを参照)、carcdrによるリストメンバーの抽出(List Elementsを参照)、vectorによるベクター作成(Vector Functionsを参照)等のようなLispデータ構造を操作する関数は提供しません。これらにたいしてはたいおう するLisp関数を呼び出すために、次のサブセクションで説明するinternfuncallを使用します。

emacs_valueオブジェクトのライフタイムはかなり短いのが普通です。このライフタイムはオブジェクトの作成に使用されたemacs_envポインターがスコープ外になると終了します。emacs_valueが望む間は行き続けるようなグローバル参照(global references)を作成を要する場合もあるかもしれません。そのようなオブジェクトの管理には以下の2つの関数を使用します。

Function: emacs_value make_global_ref (emacs_env *env, emacs_value value)

この関数はvalueのグローバル参照をリターンする。

Function: void free_global_ref (emacs_env *env, emacs_value global_value)

この関数は以前にmake_global_refで作成したglobal_valueを解放する。global_valueはこの呼び出し後は無効になる。モジュールのコードではmake_global_refと対応するfree_global_refの呼び出しそれぞれをペアーとすること。

後でモジュール関数に渡す必要があるCデータ構造体を追跡するための代替え手段はユーザーポインター(user pointer)オブジェクトの作成です。ユーザーポインター(またはuser-ptr )はCポインターをカプセル化したLispオブジェクトであり、関連付けられたファイナライザー(オブジェクトがガーベージコレクトされる際に呼び出される。Garbage Collectionを参照)をもつことができます。モジュールAPIuser-ptrオブジェクトの作成やアクセスを行う関数を提供します。これらの関数はuser-ptrオブジェクトを表現しないemacs_valueで呼び出されるとエラーコンディションwrong-type-argumentをraiseします。

Function: emacs_value make_user_ptr (emacs_env *env, emacs_finalizer fin, void *ptr)

この関数はCポインターptrをラップしたuser-ptrオブジェクトを作成してリターンする。ファイナライザー関数finNULL (ファイナライザーなし)、または以下のシグネチャをもつ関数のいずれか:

typedef void (*emacs_finalizer) (void *ptr);

finNULLポインターでなければ、user-ptrオブジェクトがガーベージコレクトされる際にptrを引数として呼び出される。Emacsの応答性を維持するためにGCは短時間で終了しなければならないので、ファイナライザーでは高価なコードの実行は行ってはならない。

Function: void *get_user_ptr (emacs_env *env, emacs_value val)

この関数はvalで表されるLispオブジェクトからCポインターを抽出する。

Function: void set_user_ptr (emacs_env *env, emacs_value value, void *ptr)

この関数はvalで表されるuser-ptrオブジェクトに埋め込まれたCポインターにptrをセットする。

Function: emacs_finalizer get_user_finalizer (emacs_env *env, emacs_value val)

この関数はvalで表されるuser-ptrオブジェクトのファイナライザー、ファイナライザーがなければNULLをリターンする。

Function: void set_user_finalizer (emacs_env *env, emacs_value val, emacs_finalizer fin)

この関数はvalで表されるuser-ptrオブジェクトのファイナライザーをfinに変更する。finNULLならuser-ptrオブジェクトのファイナライザーはなくなる。


Next: , Previous: , Up: Writing Dynamic Modules   [Contents][Index]