(This is Part 10 of a series of posts on pLisp)
The foreign functions interface in pLisp is the gateway to access and use external code. It is defined by two special forms, LOAD-FOREIGN-LIBRARY and CALL-FOREIGN-FUNCTION. The first form takes a string (literal or otherwise) that represents the name of the shared library to load, and does a dlopen() on it. The successfully created handle to the library is stored internally by pLisp, and is used to service requests through the second form. The second form is the actual invocation of the library's exported function.
We use libffi for implementing these special forms.
The data types supported are integer, float, character and string, and pointers to these data types.
To illustrate with an example, if we have a shared library called libtest.so that exposes a function called fn_ret_float:
float fn_ret_float(int i, float f, char c, char *s)
printf("%f\n", f + f);
we invoke the function as:
Welcome to pLISP. Type 'quit' to exit.
USER> (load-foreign-library "libtest.so")
USER> (call-foreign-function "fn_ret_float" 'float
The first parameter to CALL-FOREIGN-FUNCTION is the name of the to-be-invoked function, expressed as a string; the next parameter is the return type of the function; this is followed by a list containing the actual parameters and their respective data types. Passing the data types too may seem redundant, since pLisp can figure out the types on its own, but we still need to differentiate between pointers and non-pointers, so we have to live with this redundancy.
int fn_arg_int_ptr(int *i)
printf("passed value is %d\n", *i);
*i = 100;
USER> (define i 10)
USER> (call-foreign-function "fn_arg_int_ptr" 'integer
passed value is 10
This illustrates the above point about the redundancy -- if we do not mention the type of the argument (integer-pointer), pLisp will not know whether we need to pass the value of i or the address of i to the function.
A couple of points with respect to libffi:
- There seems to be some issues with using the ffi_arg struct for returning floats; I had to explicitly use a float variable for this.
- libffi is also used to implement the FORMAT special form. It handles the variable arguments requirement perfectly. More on FORMAT later.