The STklos Foreign Function Interface (FFI for short) has been
defined to allow an easy access to functions written in C without
needing to build C-wrappers and, consequently, without any need to
write C code. Note that the FFI is very machine dependent and that it
works only on a limited set of architectures/systems 1. Moreover,
since FFI allows very low level access, it is easy to crash the
STklos VM when using an external C function.
Note that the support for FFI is still minimal and that it will
evolve in future versions.
The definition of an external function is done with the
define-external special form. This form takes as arguments
a typed list of parameters and accepts several options to define the
name of the function in the C world, the library which defines this
function, ... The type of the function result and the type of its
arguments are defined in Figure 2. This table lists
the various keywords reserved for denoting types and their equivalence
between the C and the Scheme worlds.
Name | Corresponding C type | Corresponding Scheme Type |
:void | void | none |
:char | char | Scheme character |
:short | short | Scheme integer |
:ushort | unsigned short | Scheme integer |
:int | int | Scheme integer |
:uint | unsined int | Scheme integer |
:long | long int | Scheme integer |
:ulong | unsigned long int | Scheme integer |
:float | float | Scheme real number |
:double | double | Scheme real number |
:boolean | int | boolean |
:pointer | void * | Scheme pointer object or Scheme string |
:string | char * | Scheme string |
:obj | void * | Any Scheme object passed as is |
Fig. 2: FFI types
(define-external name parameters option) | STklos syntax |
The form define-external binds a new procedure to name .
The arity of this new procedure is defined by the typed list of
parameters given by parameters . This parameters list is a list
of keywords (as defined in the previous table) or couples whose first
element is the name of the parameter, and the second one is a type
keyword. All the types defined in the above table, except
:void , are allowed for the parameters of a foreign function.
Define-external accepts several options:
-
:return-type is used to define the type of the value returned
by the foreign function. The type returned must be chosen in the types specified
in the table. For instance:
(define-external maximum(:int :int)
:return-type :int)
|
defines the foreign function maximum which takes two C integers and
returns an integer result. Omitting this option default to a result
type equal to :void (i.e. the returned value is undefined).
-
:entry-name is used to specify the name of the foreign
function in the C world. If this option is omitted, the entry-name is
supposed to be name . For instance:
(define-external minimum((a :int) (b :int))
:return-type :int
:entry-name "min")
|
defines the Scheme function minimum whose application
executes the C function called min .
-
:library-name is used to specify the library which contains the
foreign-function. If necessary, the library is loaded before calling the
C function. So,
(define-external minimum((a :int) (b :int))
:return-type :int
:entry-name "min"
:library-name "libminmax")
|
defines a function which will execute the function min
located in the library libminmax.xx (where xx is the suffix used
for shared libraries on the running system (generally so ))
|
Hereafter, there are some commented definitions of external functions:
(define-external isatty ((fd :int))
:return-type :boolean)
(define-external system ((cmd :string))
:return-type :int)
(define-external ttyname (:int)
:return-type :string)
|
All these functions are defined in the C standard library, hence it is not
necessary to specify the :library-name option.
- istty is declared here as a function which takes an
integer and returns a boolean (in fact, the value returned by the
C function isatty is an int, but we ask here to the FFI
system to translate this result as a boolean value in the Scheme
world).
- system is a function which takes a string as parameter
and returns an int.
- ttyname is a function whih takes an int and returns a string.
Note that in this function the name of the parameter has been omitted
as within C prototypes.
If an external function receives an :int argument and is passed a Scheme bignum,
which then doesn't fit a long int in C, the external function will signal
an error. When a :float or :double argument is declared and is passed a
Scheme real that requires so many bits so as to not be representable in that type, that
argument will be silently taken as infinity.
(define-external c-abs ((fd :int))
:entry-name "abs"
:return-type :int)
(define-external c-fabs ((fd :double))
:entry-name "fabs"
:return-type :double)
(define-external c-fabsf ((fd :float))
:entry-name "fabsf"
:return-type :float)
|
(c-abs (- (expt 2 70))) ⇒ Error
(c-fabs -1.0e+250) ⇒ 1e+250
(c-fabsf -1.0e+250) ⇒ +inf.0
(c-fabs (- (expt 10 300))) ⇒ 1e+300
(c-fabs (- (expt 10 600))) ⇒ +inf.0
|
TODO: describe malloc and malloc_atomic and their
interaction with the GC