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] [関数]
2016-04-05