![]() |
|
Functions written in C without concern about linking with EusLisp can be loaded onto EusLisp, too. These functions are called foreign functions. Such programs are loaded by load-foreign macro which returns an instance of foreign-module. External symbol definitions in the object file is registered in the module object. Defforeign is used to make entries to C functions to be called from EusLisp. Defun-c-callable defines lisp functions callable from C. C-callable functions have special code piece called pod-code for converting parameters and transferring control to the corresponding EusLisp function. Pod-address returns the address of this code piece which should be informed to C functions.
Here is an example of C program and its interface functions to 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
Data representations in EusLisp are converted to those of C in the following manners: EusLisp's 30-bits integer (including character) is sign-extended and passed to a C function via stack. 30-bit float is extended to double and passed via stack. As for string, integer-vector and float-vector, only the address of the first element is passed on the stack, and the entire array remains uncopied. A string always has a null code at the end. EusLisp does not know how to pass arrays of more than one dimension. Every array of more than one dimension has one dimensional vector that holds all the elements linearly. This vector is obtained by array-entity macro. Also, note that a two-dimensional matrix should be transposed if it is sent to the FORTRAN subroutines, since rows and columns are ordered oppositely in FORTRAN.
Since EusLisp's representation of floating-point numbers is always single precision, conversion is required when you pass a vector of double precision floating point numbers. The conversion functions, double2float and float2double are provided for this purpose in clib/double.c. For an instance, if you have a 3x3 float-matrix and wish to pass it to a C function named CF as a matrix of double, use the following forms.
(setq mat (make-matrix 3 3))
(CF (float2double (array-entity mat)))
Struct in C can be defined by the defcstruct macro. Defcstruct accepts struct-name followed by field definition forms.
(defcstruct <struct-name>
{(<field> <type> [*] [size])}*)
For example, following struct definition is represented by the next defcstruct.
/* 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) [macro]
As shown below, the intermediate symbol file can be removed by unix:unlink. However, if you are loading more than one foreign modules both of which refer to the same library, and if you want to avoid loading the library duplicatedly, you have to use symbol-input argument. Suppose you have loaded all the functions in "linpack.a" in the above example and you are going to load another file "linapp.o" that calls functions in "linpack.a". The following call of load-foreign should be issued before you unlink "euslinpack". (load-foreign "linapp.o" :symbol-input "euslinpack") See *eusdir*/llib/linpack.l for more complete examples of load-foreign and defforeign.
(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 [macro]