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表現は、いつも単精度であるので、倍精度の実数のベクトルに渡すとき変換を要する。 変換関数、double2floatとfloat2doubleは、この目的で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) [マクロ]
以下に示されるように、中間の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-foreignとdefforeignのもっと完全な例は、*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 [マクロ]
defun-c-callable funcname paramspec resulttype . body [マクロ]
array-entity array-of-more-than-one-dimension [マクロ]
float2double float-vector [doublevector] [関数]
double2float doublevector [float-vector] [関数]
k-okada 2013-05-21