Next: , Previous: , Up: GNU Emacs Internals   [Contents][Index]


E.7 Emacsプリミティブの記述

LispプリミティブとはCで実装されたLisp関数です。Lispから呼び出せるようにC関数インターフェースの詳細はCのマクロで処理されます。新たなCコードの記述のしかたを真に理解するにはソースを読むのが唯一の方法ですが、ここではいくつかの事項について説明します。

スペシャルフォームの例として以下はeval.corです(通常の関数は同様の一般的な外観をもつ)。

DEFUN ("or", For, Sor, 0, UNEVALLED, 0,
  doc: /* Eval args until one of them yields non-nil, then return
that value.
The remaining args are not evalled at all.
If all args return nil, return nil.
usage: (or CONDITIONS...)  */)
  (Lisp_Object args)
{
  Lisp_Object val = Qnil;

  while (CONSP (args))
    {
      val = eval_sub (XCAR (args));
      if (!NILP (val))
        break;
      args = XCDR (args);
      maybe_quit ();
    }

  return val;
}

ではDEFUNマクロの引数について詳細に説明しましょう。以下はそれらのテンプレートです:

DEFUN (lname, fname, sname, min, max, interactive, doc)
lname

これは関数名として定義するLispシンボル名。上記例ではor

fname

これは関数のC関数名。これはCコードでその関数を呼び出すために使用される名前。名前は慣習として‘F’の後にLisp名をつけて、Lisp名のすべてのダッシュ(‘-’)をアンダースコアに変更する。つまりCコードから呼び出す場合にはForを呼び出す。

sname

これはLispでその関数を表すsubrオブジェクト用にデータ保持のための構造体に使用されるC変数名。この構造体はそのシンボルを作成してそれの定義にsubrオブジェクトを格納する初期化ルーチンでLispシンボル名を伝達する。慣習により常にfnameの‘F’を‘S’に置き換えた名前になる。

min

これは関数が要求する引数の最小個数。関数orは最小で0個の引数を受け入れる。

max

これは関数が受け入れる引数の最大個数が定数なら引数の最大個数。あるいはUNEVALLEDなら未評価の引数を受け取るスペシャルフォーム、MANYなら評価される引数の個数に制限がないことを意味する(&restと等価)。UNEVALLEDMANYはいずれもマクロ。maxが数字ならminより大きく8より小さいこと。

interactive

これはLisp関数でinteractive (Using Interactiveを参照)の引数として使用されるようなインタラクティブ仕様(文字列)。orの場合は0(nullポインター)でありorがインタラクティブに呼び出せないことを示す。値""はインタラクティブに呼び出す際に関数が引き受けるべきではないことを示す。値が‘"(’で始まる場合には、その文字列はLispフォームとして評価される。たとえば:

DEFUN ("foo", Ffoo, Sfoo, 0, UNEVALLED, 0
       "(list (read-char-by-name \"Insert character: \")\
              (prefix-numeric-value current-prefix-arg)\
              t))",
  doc: /* … */)
doc

これはドキュメント文字列。複数行を含むために特別なことを要しないので、これにはCの文字列構文ではなくCコメント構文を使用する。‘doc:’の後のコメントはドキュメント文字列として認識される。コメントの開始と終了の区切り文字‘/*’と‘*/’はドキュメント文字列の一部にはならない。

ドキュメント文字列の最後の行がキーワード‘usage:’で始まる場合には、その行の残りの部分は引数リストをドキュメント化するためのものとして扱われる。この方法によりCコード内で使用される引数名とは異なる引数名をドキュメント文字列内で使用することができる。その関数の引数の個数に制限がなければ‘usage:’は必須。

Lispコードでのドキュメント文字列にたいするすべての通常ルール(Documentation Tipsを参照)はCコードのドキュメント文字列にも適用される。

以下のようにドキュメント文字列の後に、そのプリミティブを実装するC関数にたいするC関数属性のリストがあるかもしれない:

DEFUN ("bar", Fbar, Sbar, 0, UNEVALLED, 0
  doc: /* … */
  attributes: attr1 attr2 …)

後に続けることにより複数の属性を指定できる。現在のところ以下の属性が認識される:

noreturn

決してリターンしないC関数を宣言する。これはC11のキーワード_Noreturn、GCCの属性__attribute__ ((__noreturn__))に対応している(Function Attributes in Using the GNU Compiler Collectionを参照)。

const

引数以外の値を検査せず、リターン値以外に影響しない関数を宣言する。これはGCCの属性__attribute__ ((__const__))に対応している。

noinline

これは関数がインラインとみなされることを抑止するGCCの属性__attribute__ ((__noinline__))に対応している。これはたとえばスタックベースの変数にたいするリンク時の最適化の効果を取り消すために必要になるかもしれない。

DEFUNマクロ呼び出しの後には、そのC関数にたいする引数リストを引数のタイプを含めて記述しなければなりません。そのプリミティブがLispで固定された最大個数をもつ引数を受け入れるならLisp引数それぞれにたいして1つのC引数をもち、各引数のタイプはLisp_Objectでなければなりません(ファイルlisp.hではタイプLisp_Objectの値を作成する種々のマクロと関数が宣言されている)。そのプリミティブのLispの最大引数個数に上限がなければ正確に2つのC引数をもたなければなりません。1つ目はLisp引数の個数で、2つ目はそれらの値を含むブロックのアドレスです。これらはそれぞれintLisp_Object *のタイプをもちます。Lisp_Objectは任意のデータ型と任意のLispオブジェクトを保持できるので実行時のみ実際のデータ型を判断できます。特定のタイプの引数だけを受け入れるプリミティブを記述したければ適切な述語を使用してタイプを明確にチェックしなければなりません(Type Predicatesを参照)。

関数For自体ではローカル変数argsはEmacsのスタックマーキングによるガーベージコレクションで制御されるオブジェクトを参照します。たとえガーベージコレクターがCのLisp_Objectスタック変数から到達可能なオブジェクトは再利用しないとしても、文字列の内容のようなオブジェクトの非オブジェクトコンポーネントは移動するかもしれないので、非オブジェクトコンポーネントにアクセスする関数はLisp評価を処理した後にはそれらのアドレスを再取得するよう注意しなければなりません。Lisp評価は直接と間接を問わずeval_subFevalの呼び出しを通じて発生する可能性があります。

ループ内部のmaybe_quit呼び出しに注意してください。この関数はユーザーがC-gを渡したかどうかをチェックして、もしそうなら処理をabortします。多数の繰り返しを要する可能性があるすべてのループ内でこれを行うべきです。この場合には引数のリストは非常に長くなるかもしれません。これはEmacsの応答性とユーザーエクスペリエンスを向上させます。

Emacsが一度ダンプされた後に変数に何か書き込まれているときには、その静的変数やグローバル変数にCの初期化を使用してはなりません。初期化されたこれらの変数はEmacsのダンプの結果として、(特定のオペレーティングシステムでは)読み取り専用となるメモリーエリアに割り当てられます。Pure Storageを参照してください。

C関数の定義だけではLispプリミティブを利用可能にするのに十分ではありません。そのプリミティブにたいしてLispシンボルを作成して関数セルに適切なsubrオブジェクトを格納しなければなりません。このコードは以下のようになるでしょう:

defsubr (&sname);

ここでsnameDEFUNの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_ObjectのC変数をLispから可視にします。DEFVAR_INTはタイプintのC変数を常に整数となる値をもつようにしてLispから可視にします。DEFVAR_BOOLはタイプintのC変数を常にtnilのいずれかとなる値をもつようにしてLispから可視にします。DEFVAR_BOOLで定義された変数はバイトコンパイラーに使用されるリストbyte-boolean-varsに自動的に追加されることに注意してください。

Cで定義されたLisp変数をdefcustomで宣言された変数のように振る舞わせたい場合には、cus-start.elに適切なエントリーを追加してください。

タイプ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, 0,
  doc: /* Return non-nil if COORDINATES are in WINDOW.
  ...
  or `right-margin' is returned.  */)
  (register Lisp_Object coordinates, Lisp_Object window)
{
  struct window *w;
  struct frame *f;
  int x, y;
  Lisp_Object lx, ly;

  CHECK_LIVE_WINDOW (window);
  w = XWINDOW (window);
  f = XFRAME (w->frame);
  CHECK_CONS (coordinates);
  lx = Fcar (coordinates);
  ly = Fcdr (coordinates);
  CHECK_NUMBER_OR_FLOAT (lx);
  CHECK_NUMBER_OR_FLOAT (ly);
  x = FRAME_PIXEL_X_FROM_CANON_X (f, lx) + FRAME_INTERNAL_BORDER_WIDTH(f);
  y = FRAME_PIXEL_Y_FROM_CANON_Y (f, ly) + FRAME_INTERNAL_BORDER_WIDTH(f);

  switch (coordinates_in_window (w, x, y))
    {
    case ON_NOTHING:            /* NOT in window at all.  */
      return Qnil;

    ...

    case ON_MODE_LINE:          /* In mode line of window.  */
      return Qmode_line;

    ...

    case ON_SCROLL_BAR:         /* On scroll-bar of window.  */
      /* Historically we are supposed to return nil in this case.  */
      return Qnil;

    default:
      abort ();
    }
}

CコードはCで記述されていなければ名前で呼び出すことはできないことに注意してください。Lispで記述された関数を呼び出すには関数funcallをCで具現化したFfuncallを使用します。Lisp関数funcallは個数制限なしの引数を受け付けるので、Cでの引数はLispレベルでの引数個数とそれらの値を含む1次元配列という2個の引数になります。Lispレベルでの1つ目の引数は呼び出す関数、残りはそれに渡す引数です。

C関数call0call1call2、...は個数が固定された引数でLisp関数を手軽に呼び出す便利な方法を提供します。これらはFfuncallを呼び出すことにより機能します。

eval.cは例を探すのに適したファイルです。lisp.hには重要なマクロと関数の定義がいくつか含まれています。

副作用をもたない関数を定義する場合には、コンパイラーのオプティマイザーに知らせるためにside-effect-free-fnsside-effect-and-error-free-fnsをバインドするbyte-opt.el内のコードを更新してください。


Next: , Previous: , Up: GNU Emacs Internals   [Contents][Index]