Foreign Language Interface

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. The string can either be a normal string or a foreign-string. A string may contain null codes, though it is guaranteed that the string also 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 correspoiding one dimensional vector that holds the entire elements linearly. This vector is obtained by the 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. For this purpose, the conversion functions, double2float and float2double are provided by clib/double.c. For an instance, if you have a 3x3 float-matrix and want to pass it to a C function named cfun as a matrix of double, use the following forms.

     (setq mat (make-matrix 3 3))
     (cfun (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]

loads an object module written in languages other than EusLisp. In Solaris2, load-foreign just calls load with a null string as its :entry parameter. A compiled-code object is returned. This result is necessary to make entries to the functions in the module by defforeign called later on. Libraries can be specified in ld-option. However, the symbols defined in the libraries cannot be captured in the default symbol-output file. In order to allow EusLisp to call functions defined in the libraries, symbol-output and symbol-file must be given explicitly. (These arguments are not needed if you are not going to call the library functions directly from EusLisp, i.e. if you are referring them only from functions in objfile). Load-foreign links objfile with libraries specified and global symbols in EusLisp which is in core, and writes the linked object in symbol-output. Then, symbols in symbol-file are searched and listed in the foreign-module. Since symbol-file is defaulted to be objfile, only the symbols defined in objfile are recognized if symbol-file is not given. To find all the global entries both in objfile and libraries, the linked (merged) symbol table resulted from the first link process of load-foreign must be examined. For this reason, an identical file name must be given both to symbol-output and to symbol-file.

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]

makes a function entry in a foreign language module. funcname is a symbol to be created in EusLisp. module is a compiled-code object returned by load-foreign. cname is the name of the C-function defined in the foreign program. It is a string like "_myfunc". paramspec is a list of parameter type specifications which is used for the data type conversion and coercion when arguments are passed from EusLisp to the C function. Paramspec can be NIL if no data conversion or type check is required. One of :integer, :float , :string, or (:string n) must be given to resulttype. :Integer means that the c function returns either char, short or int (long). :Float should be specified both for float and double. :String means the C function returns a pointer to a string, and EusLisp should add a long-word header to the string to accomodate it as a EusLisp string. The length of the string is found by strlen. Note that the writing a header just before the string may cause a disastrous result. On the other hand, (:string n) is safer but slower because a EusLisp string of length n is newly created and the contents of C string is copied there. (:string 4) should be used for a C function that returns a pointer to an integer. The resulted integer value of the result can be obtained by (sys:peek result :long), where result is a variable set to the result of the C function. You may also specify (:foreign-string [n]) for C functions that return a string or a struct. The result is a foreign-string whose content is held somewhere outside EusLisp control. If the result string is null-terminated and the length of the string is known by strlen, you don't need to specify the length [n]. However, if the result contains null codes, which is usual for structs, the length of the foreign-string should be explicitly given. Whether you should use (:string n) or (:foreign-string n) is not only the matter of speed, but the matter of structure sharing. The difference is whether the result is copied or not.

Fortran users should note that every argument to a Fortran function or a subroutine is passed by call-by-reference. Therefore, even a simple integer or float type argument must be put in a integer-vector or a float-vector before it is passed to Fortran.



defun-c-callable funcname paramspec resulttype . body [macro]

defines a EusLisp function that can be called from foreign language code. funcname is a symbol for which a EusLisp function is defined. paramspec is a list of type specifiers as in defforeign. Unlike defforeign's paramspec, defun-c-callable's paramspec cannot be omitted unless the function does not receive any argument. :integer should be used for all of int, short and char types and :float for both of float and double. resulttype is the type of the Lisp function. resulttype can be omitted unless you need type check or type coercion from integer to float. body is lisp expressions that are executed when this function is called from C. The function defined by defun-c-callable can be called from Lisp expressions, too. Defun-c-callable returns funcname. It looks like a symbol, but it is not, but an instance of foreign-pod which is a subclass of symbol.


pod-address funcname [function]

returns the address of a foreign-to-EusLisp interface code of the c-callable Lisp function funcname defined by defun-c-callable. This is used to inform a foreign language program of the location of a Lisp function.


array-entity array-of-more-than-one-dimension [macro]

returns one-dimensional vector which holds all the elements of a multi-dimensional array. This is needed to pass a multi-dimensional or general array to a foreign function, although a simple vector can be passed directly.


float2double float-vector [doublevector] [function]

converts a float-vector to double precision representation. The result is of type float-vector but the length is twice as much as the first argument.


double2float doublevector [float-vector] [function]

A vector of double precision numbers is converted to single precision float-vector.


k-okada 2013-05-21