Next: , Previous: Memory Usage, Up: GNU Emacs Internals


B.5 Emacs基本関数の書き方

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)
lname
関数名として定義するLispシンボルの名前である。 上の例では、orである。
fname
この関数のCの関数としての名前である。 この関数をCのコードから呼び出すときに使う名前である。 慣習により、Lispでの名前のまえに‘F’を付けるが、 Lispでの名前のダッシュ(‘-’)はすべて下線に置き換える。 したがって、Cのコードからこの関数を呼び出すには、Forを呼び出す。 引数はLisp_Object型である必要があることに注意してほしい。 ファイルlisp.hでは、 Lisp_Object型の値を作成するためのさまざまなマクロや関数を宣言してある。
sname
これは、Lispでの関数を表現するsubrオブジェクト向けのデータを保持する 構造体に使うCの変数名である。 この構造体は、シンボルを作成しその定義としてsubrオブジェクトを保存する 初期化ルーティンへLispのシンボル名を運ぶ構造体である。 慣習により、この名前はつねに fnameの‘F’を‘S’に置き換えたものである。
min
関数が必要とする引数の最小の個数である。 関数orは最小0個の引数を許す。
max
関数が許す引数の最大の個数に制限があるときの引数の最大の個数である。 あるいは、評価していない引数を受け取るスペシャルフォームであることを 表すUNEVALLED、 評価済みの引数を何個でも受け取ることを表すMANY&restに等価)でもよい。 UNEVALLEDMANYもマクロである。 maxが数であるときには、 それはminより小さくなく、かつ、7より大きくないこと。
interactive
この関数の対話指定であり、 Lisp関数においてinteractiveの引数に使う文字列である。 orの場合には0(空ポインタ)であり、 orは対話的に呼び出せないことを表す。 値""は、対話的に呼び出されると この関数は引数を受け取らないことを表す。
doc
説明文字列である。 各行末に‘\n\’と書く必要があることを除けば、 Lispで定義する関数の説明文字列のように書く。 特に、最初の行は1つの文であること。

マクロDEFUNの呼び出しのあとには、 Cの関数に必須な引数名の並びを書き、引数に対する普通のCの宣言を続けます。 引数の最大個数が固定されている関数では、 各Lisp引数向けにCの引数宣言を書き、 それらをすべてLisp_Object型にします。 Lisp関数に引数の個数に上限がないとき、 それを実装するCの関数は実際には2つの引数を受け取ります。 第1引数はLisp引数の個数であり、 第2引数はそれらの値を収めたブロックのアドレスです。 引数の型はintLisp_Object *です。

関数For自身の内側では、 マクロGCPRO1UNGCPROを使っていることに注意してください。 GCPRO1は、ガベッジコレクションから変数を『保護』するために使います。 つまり、ガベッジコレクタに対してこの変数を調べてその内容を 参照可能なオブジェクトとみなすように指示します。 FevalFevalを直接/間接的に呼び出すものを呼ぶときには、 このようにする必要があります。 そのような場面では、再度参照する意図がある任意のLispオブジェクトは 保護する必要があります。 UNGCPROは、この関数での変数の保護を取り消します。 これは明示的に行う必要があります。

ほとんどのデータ型では、少なくともそのオブジェクトへの1つのポインタを 保護すれば十分であり、そのオブジェクトに循環がない限り、 そのオブジェクトへのすべてのポインタは正しく保たれます。 文字列にはこれはあてはまりません。 ガベッジコレクタがそれらを移動するからです。 ガベッジコレクタが文字列を移動すると、 それに対する既知のポインタをすべて再配置し、 それ以外のポインタは不正になります。 したがって、ガベッジコレクタが動く可能性のある任意の部分では、 文字列へのすべてのポインタを保護する必要があります。

マクロGCPRO1は1つのローカル変数のみを保護します。 2つ保護したい場合にはかわりにGCPRO2を使います。 GCPRO1を繰り返しても働きません。 GCPRO3GCPRO4のマクロもあります。

これらのマクロはgcpro1などのローカル変数を暗黙のうちに使いますが、 読者はこれらを型struct gcproで明示的に宣言する必要があります。 したがって、GCPRO2を使う場合には、 gcpro1gcpro2を宣言する必要があります。 残念ですが、ここではすべての詳細は説明しきれません。

Emacsをいったんダンプしたあとでも静的やグローバルな変数に書き込むのであれば、 それらの変数にはCの初期化構文を使ってはいけません。 初期化構文を伴うそれらの変数は、Emacsをダンプすると (オペレーティングシステムによっては)その結果として 読み出し専用のメモリ領域に割り当てられます。 See Pure Storage

関数の内側では静的変数を使わずに、 すべての静的変数はファイルのトップレベルに置きます。 オペレーティングシステムによっては Emacsはキーワードstaticを空のマクロと定義することもあるので、 これは必要なことなのです。 (このような定義を使うのは、そのようなシステムは、 初期化構文があろうとなかろうと静的と宣言した変数を ダンプ後には読み出し専用にしてしまうからである。)

Cの関数を定義しただけではLisp基本関数としては使えません。 基本関数に対するLispシンボルを作成し、 その関数セルに適切なsubrオブジェクトを保存する必要があります。 そのコードはつぎのようになります。

     defsubr (&subr-structure-name);

ここで、subr-structure-nameDEFUNの第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からはtnilを値として見える 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の関数、call0call1call2などは、 固定個数の引数を受け取るLisp関数を簡便に呼び出す手軽な方法です。 これらはFfuncallを呼び出して動作します。

eval.cは例を調べるにはとてもよいファイルです。 lisp.hには重要なマクロや関数の定義が入っています。