次: Object Internals, 前: Memory Usage, 上: GNU Emacs Internals
Lisp基本関数は、Cで実装したLisp関数です。 Lispから呼び出すためのCの関数とのインターフェイスの詳細は、 数個のCのマクロで処理しています。 新たにCのコードを書く方法をほんとうに理解する唯一の方法は、 ソースを読むことですが、ここではその一部を説明します。
スペシャルフォームの例は、eval.cから引用したor
の定義です。
(普通の関数も同じように見える。)
DEFUN ("or", For, Sor, 0, UNEVALLED, 0, "Eval args until one of them yields non-nil; return that value.\n\ The remaining args are not evalled at all.\n\ If all args return nil, return nil.") (args) Lisp_Object args; { register Lisp_Object val; Lisp_Object args_left; struct gcpro gcpro1; if (NULL (args)) return Qnil; args_left = args; GCPRO1 (args_left); do { val = Feval (Fcar (args_left)); if (!NULL (val)) break; args_left = Fcdr (args_left); } while (!NULL (args_left)); UNGCPRO; return val; }
マクロDEFUN
の引数の詳しい説明から始めます。
その雛型はつぎのとおりです。
DEFUN (lname, fname, sname, min, max, interactive, doc)
or
である。
For
を呼び出す。
引数はLisp_Object
型である必要があることに注意してほしい。
ファイルlisp.hでは、
Lisp_Object
型の値を作成するためのさまざまなマクロや関数を宣言してある。
or
は最小0個の引数を許す。
UNEVALLED
、
評価済みの引数を何個でも受け取ることを表すMANY
(&rest
に等価)でもよい。
UNEVALLED
もMANY
もマクロである。
maxが数であるときには、
それはminより小さくなく、かつ、7より大きくないこと。
interactive
の引数に使う文字列である。
or
の場合には0(空ポインタ)であり、
or
は対話的に呼び出せないことを表す。
値""
は、対話的に呼び出されると
この関数は引数を受け取らないことを表す。
マクロDEFUN
の呼び出しのあとには、
Cの関数に必須な引数名の並びを書き、引数に対する普通のCの宣言を続けます。
引数の最大個数が固定されている関数では、
各Lisp引数向けにCの引数宣言を書き、
それらをすべてLisp_Object
型にします。
Lisp関数に引数の個数に上限がないとき、
それを実装するCの関数は実際には2つの引数を受け取ります。
第1引数はLisp引数の個数であり、
第2引数はそれらの値を収めたブロックのアドレスです。
引数の型はint
とLisp_Object *
です。
関数For
自身の内側では、
マクロGCPRO1
とUNGCPRO
を使っていることに注意してください。
GCPRO1
は、ガベッジコレクションから変数を『保護』するために使います。
つまり、ガベッジコレクタに対してこの変数を調べてその内容を
参照可能なオブジェクトとみなすように指示します。
Feval
やFeval
を直接/間接的に呼び出すものを呼ぶときには、
このようにする必要があります。
そのような場面では、再度参照する意図がある任意のLispオブジェクトは
保護する必要があります。
UNGCPRO
は、この関数での変数の保護を取り消します。
これは明示的に行う必要があります。
ほとんどのデータ型では、少なくともそのオブジェクトへの1つのポインタを 保護すれば十分であり、そのオブジェクトに循環がない限り、 そのオブジェクトへのすべてのポインタは正しく保たれます。 文字列にはこれはあてはまりません。 ガベッジコレクタがそれらを移動するからです。 ガベッジコレクタが文字列を移動すると、 それに対する既知のポインタをすべて再配置し、 それ以外のポインタは不正になります。 したがって、ガベッジコレクタが動く可能性のある任意の部分では、 文字列へのすべてのポインタを保護する必要があります。
マクロGCPRO1
は1つのローカル変数のみを保護します。
2つ保護したい場合にはかわりにGCPRO2
を使います。
GCPRO1
を繰り返しても働きません。
GCPRO3
やGCPRO4
のマクロもあります。
これらのマクロはgcpro1
などのローカル変数を暗黙のうちに使いますが、
読者はこれらを型struct gcpro
で明示的に宣言する必要があります。
したがって、GCPRO2
を使う場合には、
gcpro1
とgcpro2
を宣言する必要があります。
残念ですが、ここではすべての詳細は説明しきれません。
Emacsをいったんダンプしたあとでも静的やグローバルな変数に書き込むのであれば、 それらの変数にはCの初期化構文を使ってはいけません。 初期化構文を伴うそれらの変数は、Emacsをダンプすると (オペレーティングシステムによっては)その結果として 読み出し専用のメモリ領域に割り当てられます。 See Pure Storage。
関数の内側では静的変数を使わずに、
すべての静的変数はファイルのトップレベルに置きます。
オペレーティングシステムによっては
Emacsはキーワードstatic
を空のマクロと定義することもあるので、
これは必要なことなのです。
(このような定義を使うのは、そのようなシステムは、
初期化構文があろうとなかろうと静的と宣言した変数を
ダンプ後には読み出し専用にしてしまうからである。)
Cの関数を定義しただけではLisp基本関数としては使えません。 基本関数に対するLispシンボルを作成し、 その関数セルに適切なsubrオブジェクトを保存する必要があります。 そのコードはつぎのようになります。
defsubr (&subr-structure-name);
ここで、subr-structure-nameはDEFUN
の第3引数に使った名前です。
すでにLisp基本関数が定義されているファイルに新たな基本関数を追加するときには、
(ファイルの末尾近くで)syms_of_
somethingという名前の関数を探し、
それにdefsubr
の呼び出しを追加します。
ファイルにこの関数がなかったり、新たなファイルを作成した場合には、
syms_of_
filename(たとえばsyms_of_myfile
)を追加します。
そして、ファイルemacs.cでこれらの関数を呼び出している箇所を探して、
そこにsyms_of_
filenameの呼び出しを追加します。
関数syms_of_
filenameは、
Lisp変数として見える任意のCの変数を定義する場所でもあります。
DEFVAR_LISP
は、Lispから見えるLisp_Object
型のCの変数を作ります。
DEFVAR_INT
は、Lispからはつねに整数を値として見える
int
型のCの変数を作ります。
DEFVAR_BOOL
は、Lispからはt
かnil
を値として見える
int
型のCの変数を作ります。
ファイルだけに有効なLisp_Object
型のCの変数を定義した場合には、
つぎのようにして、syms_of_
filenameの中でstaticpro
を
呼び出してその変数をガベッジコレクションから保護する必要があります。
staticpro (&variable);
つぎは、少々複雑な引数を取る別の関数の例です。 これはwindow.cから取ったもので、 マクロとLispオブジェクトを操作する関数の使い方を例示します。
DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p, Scoordinates_in_window_p, 2, 2, "xSpecify coordinate pair: \nXExpression which evals to window: ", "Return non-nil if COORDINATES is in WINDOW.\n\ COORDINATES is a cons of the form (X . Y), X and Y being distances\n\ ... If they are on the border between WINDOW and its right sibling,\n\ `vertical-line' is returned.") (coordinates, window) register Lisp_Object coordinates, window; { int x, y; CHECK_LIVE_WINDOW (window, 0); CHECK_CONS (coordinates, 1); x = XINT (Fcar (coordinates)); y = XINT (Fcdr (coordinates)); switch (coordinates_in_window (XWINDOW (window), &x, &y)) { case 0: /* NOT in window at all. */ return Qnil; case 1: /* In text part of window. */ return Fcons (make_number (x), make_number (y)); case 2: /* In mode line of window. */ return Qmode_line; case 3: /* On right border of window. */ return Qvertical_line; default: abort (); } }
Cのコードでは、関数がCで定義されていない限り、
関数をその名前で呼び出せないことに注意してください。
Lispで書かれた関数を呼び出す方法は、Lispの関数funcall
を
内蔵するFfuncall
を使うことです。
Lisp関数funcall
は任意個数の引数を受け付けるので、
Cでは2つの引数、Lispレベルの引数の個数と
それらの値を収めた一次元の配列を受け取ります。
Lispレベルの最初の引数は呼び出すべきLisp関数であり、
残りはそれに渡す引数です。
Ffuncall
はエバリュエータを呼び出すので、
Ffuncall
を呼び出す周りでは、
ガベッジコレクションからポインタを保護する必要があります。
Cの関数、call0
、call1
、call2
などは、
固定個数の引数を受け取るLisp関数を簡便に呼び出す手軽な方法です。
これらはFfuncall
を呼び出して動作します。
eval.cは例を調べるにはとてもよいファイルです。 lisp.hには重要なマクロや関数の定義が入っています。