他言語インターフェース

Euslispとのリンクを考慮していないCの関数もEuslispにロードすることができる。 これらの関数は、他言語関数と呼ばれる。 そのようなプログラムはload-foreignマクロによりロードされる。 そのマクロは、foreign-moduleのインスタンスを返す。 オブジェクトファイルの中の外部symbol定義は、モジュールオブジェクトの中に 登録されている。 defforeignは、Euslispから呼び出すための C関数に対するエントリーを作るために使用される。 defun-c-callableは、Cから呼び出し可能なlisp関数を定義する。 呼び出し可能なCの関数は、パラメータを変換し関連するEuslispの関数へ制御を移すために pod-codeと呼ばれる特別なコードを持つ。 pod-addressは、この特別なコードのアドレスを返す。 そのアドレスはCの関数に通知されるべきである。

これは、CのプログラムのサンプルとEuslispへの関数インターフェースである。

/* C program named cfunc.c*/

static int (*g)();	/* variable to store Lisp function entry */

double sync(x)
double x;
{ extern double sin();
  return(sin(x)/x);}

char *upperstring(s)
char *s;
{ char *ss=s;
  while (*s) { if (islower(*s)) *s=toupper(*s); s++;}
  return(ss);}

int setlfunc(f)      /* remember the argument in g just to see */
int (*f)();          /* how Lisp function can be called from C */
{ g=f;}

int callfunc(x)      /* apply the Lisp function saved in g to the arg.*/
int x;
{ return((*g)(x));}

;;;; Example program for EusLisp's foreign language interface
;;;; make foreign-module
(setq m (load-foreign "cfunc.o"))

;; define foreign functions so that they can be callable from lisp
(defforeign sync m "sync" (:float) :float)
(defforeign toupper m "upperstring" (:string) :string)
(defforeign setlfunc m "setlfunc" (:integer) :integer)
(defforeign callfunc m "callfunc" (:integer) :integer)

;; call them
(sync 1.0)	--> 0.841471
(print (toupper "abc123"))  --> "ABC123"

;; define a test function which is callable from C.
(defun-c-callable TEST ((a :integer)) :integer
      (format t "TEST is called, arg=~s~%" a)
      (* a a))    ;; return the square of the arg
;;  call it from C
;;setlfunc remembers the entry address of Lisp TEST function.
(setlfunc (pod-address (intern "TEST")))
(callfunc 12)  --> TEST is called, arg=12  144

Euslispのデータ表現は、以下に示す方法でCのデータ表現に変換される。 EusLispの30ビット整数(文字列を含む)は、符号拡張され、スタックを通してCの関数に渡される。 30ビット実数は、倍精度実数(double)に拡張され、スタックを通して渡される。 文字列と整数ベクトルと実数ベクトルについては、その最初の要素のアドレスのみが スタックに渡され、行列自体はコピーされない。 Euslispには、2次元以上の配列を渡す方法がない。 2次元以上の配列はすべての要素を線形に保持する1次元ベクトルを持つ。 このベクトルは、array-entityマクロにより得られる。 もし、2次元行列をFORTRANのサブルーチンに送る場合、FORTRANにおいて列と行が反対となっているため その行列を転置しなければならないことに注意すること。

実数のEuslisp表現は、いつも単精度であるので、倍精度の実数のベクトルに渡すとき変換を要する。 変換関数、double2floatfloat2doubleは、この目的でclib/double.cの中に定義されている。 例えば、もし3x3の実数行列があり、CFのいう名のCの関数にそれを倍精度実数の行列として渡したいなら、 以下のように使用すればよい。

     (setq mat (make-matrix 3 3))
     (CF (float2double (array-entity mat)))

Cの構造体は、defstructマクロにより定義することができる。 defstructは、次のようなフィールド定義書式によりstruct-nameを受け取る。

     (defcstruct <struct-name>
        {(<field> <type> [*] [size])}*)
たとえば、以下に示す構造体の定義は、つぎのdefstructによって表現される。
     /* C definition */
     struct example {
        char  a[2];
        short b;
        long  *c;
        float *d[2];};

     /* equivalent EusLisp definition */
     (defcstruct example
        (a :char 2)
        (b :short)
        (c :long *)
        (d :float * 2))



load-foreign objfile &key symbol-input symbol-output (symbol-file objfile) ld-option) [マクロ]

Euslisp以外の言語で書かれたオブジェクトモジュールをロードする。 Solaris2において、load-foreign:entryパラメータにnull文字列を 与えたloadを呼び出す。 コンパイルされたコードのオブジェクトが返される。 この結果は、後にdefforeignを呼び出すことによって モジュールの中の関数のエントリーを作ることが 必要である。 ライブラリーはld-optionに指定することができる。 しかしながら、ライブラリの中に定義されたsymbolはデフォルトのsymbol-outputファイルで 獲得することができない。 ライブラリで定義された関数の呼び出しをEuslispに許可するために、 symbol-outputsymbol-fileが明示的に与えられなければならない。 (もし、objfileからのみそれらを参照するならば、これらの引き数は必要ない。) load-foreignは、指定されたライブラリとグローバル変数と一緒にobjfileをEuslispのコアにリンクし、 リンクされたオブジェクトをsymbol-outputに書き込む。 それから、symbol-fileの中のsymbolは、検索され、他言語モジュールの中にリストアップされる。 symbol-fileのデフォルトがobjfileであるので、もしsymbol-fileが与えられないなら、 objfileに定義されているsymbolのみ認識される。 objfileとライブラリの両方のグローバルエントリーをすべて見るために、 load-foreignの最初のプロセスリンクの結果であるリンクされた(マージされた)symbolテーブル は確かめられなければならない。このような理由で、symbol-outputsymbol-fileの両方に 同一のファイル名を与えなければならない。

以下に示されるように、中間のsymbolファイルはunix:unlinkによって 削除することができる。 しかしながら、もし同じライブラリを参照する2つ以上の他言語モジュールをロードするとき、 ライブラリの2重化を避けたいなら、symbol-output引き数を使用しなければならない。 上記の例として、"linpack.a"のすべての関数をロードしており、 次に"linpack.a"の関数を呼び出している他のファイル"linapp.o"を呼びだそうとしていると仮定する。 次のload-foreign呼び出しは、"euslinpack"をunlinkする前に発行しなければならない (load-foreign "linapp.o" :symbol-input "euslinpack")load-foreigndefforeignのもっと完全な例は、*eusdir*/llib/linpack.lで 見ることができる。



(setq linpack-module
    (load-foreign "/usr/local/eus/clib/linpackref.o"
        :ld-option  "-L/usr/local/lib -llinpack -lF77 -lm -lc"
        :symbol-output "euslinpack"
        :symbol-file "euslinpack"
        ))
(unix:unlink "euslinpack")

defforeign funcname module cname paramspec resulttype [マクロ]

他言語モジュールの中の関数エントリーを作る。 funcnameは、Euslispに作られるsymbolである。 moduleは、load-foreignによって返されるコンパイルされたコードのオブジェクトである。 cnameは、他言語プログラムの中で定義されているCの関数の名前である。 その名前は"_myfunc"のような文字列である。 paramspecは、パラメータの型指定のリストである。 それは、EuslispからCの関数に引き数を渡すときに、データの型変換と強制(coercion)を行うために使用される。 データ変換がなかったり、あるいは型チェックが必要ないとき、paramspecはNILで構わない。 :integer, :float, :string, (:string n)の内の1つがresulttypeに与えられなければならない。 :integerは、Cの関数がchar,short,int(long)のいずれかを返すことを意味する。 :floatは、返す値がfloatあるいはdoubleのときに指定する。 :stringは、C関数がstringへのポインターを返すことを意味し、 EuslispはEuslispの文字列に変更するためにstringにlong-wordのヘッダーを追加する。 文字列の長さはstrlenによって見つけられる。 stringの直前に書き込まれるヘッダーは、悲惨な結果を引き起こすことがあることに注意。 もう一方で、(:string n)は、安全だが遅い。なぜなら、 nの長さを持つEuslispの文字列が新しく作成され、Cの文字列の内容が そこにコピーされるからである。 (:string 4)は、整数へのポインターを返すCの関数に使用できる。 FORTRANユーザーは、FORTRANの関数あるいはサブルーチンのあらゆる引き数は、 call-by-refferenceによって渡されることに注意すべきである。 したがって、1つの整数あるいは実数型の引き数でさえFORTRANへ 渡される前に整数ベクトルあるいは実数ベクトルに 置かれなければならない。


defun-c-callable funcname paramspec resulttype . body [マクロ]

他言語のコードから呼び出すことができるEuslispの関数を定義する。 funcnameは、Euslispの関数として定義されているsymbolである。 paramspecは、defforeignの中の型指定のリストである。 defforeignのparamspecと違い、defun-c-callableのparamspecは、 関数が引き数を全く受け取らない場合以外、省略することができない。 :integerは、int,char,shortのすべての型に使用すべきである。 :floatは、floatとdoubleに使用する。 resulttypeは、Lisp関数の型である。 resulttypeは、型のチェックあるいは整数から実数に型の強制を 必要とする場合を除いて、省略することができる。 bodyは、この関数がCから呼び出されるとき、実行されるlisp表現である。 defun-c-callableで定義されている関数は、Lisp表現からでも 呼び出すことができる。 defun-c-callablefuncnameを返す。 その返り値は、symbolのようであるが、そうではなく、symbolのサブクラスである foreign-podのインスタンスである。


pod-address funcname [関数]

defun-c-callableで定義されたCで呼び出し可能なLisp関数funcnameにおける 他言語とEuslispとのインターフェースコードのアドレスを返す。 これは、他言語プログラムにLisp関数の位置を知らせるために使用される。


array-entity array-of-more-than-one-dimension [マクロ]

多次元配列の要素を保持する1次元ベクトルを返す。 これは、多次元あるいは一般の配列を他言語に渡すために必要である。 しかし、1次元のベクトルは直接渡すことができる。


float2double float-vector [doublevector] [関数]

float-vectorを倍精度実数の表現に変換する。 その結果は、float-vectorであるが、最初の引き数の長さの2倍になっている。


double2float doublevector [float-vector] [関数]

倍精度実数表現が単精度のfloat-vectorに変換される。


2016-04-05