4.3 The C-to-FFI facility
translate-c-file
c-filename&key
:output-file :case-sensitive :defines-too :compile :thing :loud :ansi-c
:compile
keyword is non-nil
.
:output-file
, it defaults to the C filename ending in .lisp, as in foo.c becoming foo.lisp. If it is the symbolt
, then output is written to*standard-output*
instead of a file.
:case-sensitive
is non-nil
, the Lisp code produced will have exactly the same case as the original C code, and that case will be preserved by the Lisp reader. This means that most symbols will be printed surrounded by vertical bars. If the value of:case-sensitive
isnil
(the default), the Lisp code produced will have the same case as the original C code, but that case will not be preserved by the Lisp reader.
:include
is supplied, it should be a list of directory names that would normally be passed tocc
as-I
arguments when compiling the given C file. This is where the#include
'd files exist. (We need this because we invoke the C preprocessorcpp
on the file.) If a value for:include
is not supplied, it defaults to the directory of c-filename.
:translate-includes
is non-nil
, the translation will contain text from files included using#include
. If it isnil
, only the text within the file specified will be translated. The value of:translate-includes
defaults tonil
. (Note that the include search path specified by:include
is still needed even if the value of:translate-includes
isnil
, because the include files may contain definitions that are necessary to process the file.)
:defines-too
ist
(the default), then we translate simplecpp
#define
statements intodefconstant
calls. This does not attempt to translate C macros that take arguments.
#include
directories. If calls totypedef
are not made before the defined types are first used, then ambiguity arises and we cannot reliably parse C. Likewise, unexpanded preprocessor macros will cause parse errors.
:compile
ist
, we callcompile-file
on the Lisp file after writing it.
:thing
is what to extract from c-filename; it defaults to:all
, meaning that it produces FFI calls for everything in the C file (and in the files#include
'd by it, if the value of:translate-includes
is non-nil
). It may be a string or list of strings, which are the names of structures, functions, or variables which we should translate. However, we also translate all user-defined structures and types which are referenced by the specified thing--if a function returns a pointer to a structure, then the definition of that structure will be translated as well.
:loud
ist
, then some progress messages will be written on*standard-output*
. If it is:very
, then a C-syntax representation of everything being parsed will be printed. This can be useful for debugging.
:ansi-c
reflects whether you use an ANSI C compiler. The value should bet
if you do andnil
if you don't. It defaults to the value of*ansi-c*
.
translate-c-file
, you must uselcl:load-foreign-files
orlcl:load-foreign-libraries
to read in the.o
or.a
file corresponding to the C code which you have translated. If you don't do this, then Lisp can't determine the addresses of the global variables referenced declared in C and can't make pointers to them.
.c
files as well as.h
files, even if all of the functions you wish to use are declared in the.h
files. Non-ANSI header files do not contain type declarations for the arguments of functions, and Lisp needs to know them.
.h
file in your system--simply running it on a file which#include
's the header files you wish to translate will do, since#include
'd files are spliced in bycpp
.
;;; Assume we begin with the C function: ;;; int ThisIsATest (); ;;; defined in the file test.c ;;; The default case-sensitive value of nil produces ;;; more readable code, but has the potential of producing ;;; ambiguous results, since C is strictly case-sensitive. ;;; Then the following Lisp code will be produced: (def-foreign-function (ThisIsATest (:name "_ThisIsATest") (:return-type :signed-32bit)))*cpp-command* Variable;;; Now we call translate-c-file with :case-sensitive true. (translate-c-file "test.c" :case-sensitive t)
(def-foreign-function (|ThisIsATest| (:name "_ThisIsATest") (:return-type :signed-32bit)))
*cpp-command*
*cpp-arguments*
, *prefer-multidimensional-arrays*
, *make-linker-name*
, *pointer-to-character-becomes-string*
*cpp-arguments*
*cpp-command*
variable.
-E
. Do not include in this list the switch that inhibits line numbering (normally-P
).
*cpp-command*
, *prefer-multidimensional-arrays*
, *make-linker-name*
, *pointer-to-character-becomes-string*
*prefer-multidimensional-arrays
*
int a[5][10];
says that the variablea
holds storage for 50 integers. The expressiona[3]
treatsa
as a one-dimensional array of arrays, and refers to the array in the third row. The expressiona[3][3]
can be viewed as either accessing slot (3, 3) in a 5x10 array, or accessing slot 3 of the array in slot 3 of a one-dimensional array.
*prefer-multidimensional-arrays*
ist
(the default), then the type ofint a[5][10]
will be(:array :signed-32bit (5 10))
. If it isnil
, then the Foreign Function Interface type will instead be(:array (:array :signed-32bit (10)) (5))
.
lcl:foreign-aref
, *cpp-arguments*
, *cpp-command*
, *make-linker-name*
, *pointer-to-character-becomes-string*
*make-linker-name
*
:name
option ofdef-foreign-function
. On Solaris, this function is an identity. This may have to be changed to match your C compiler.
> (apply c-to-ffi:*make-linker-name* '("my-function")) "my-function"
*cpp-arguments*
, *cpp-command*
, *prefer-multidimensional-arrays*
, *pointer-to-character-becomes-string*
*pointer-to-character-becomes-string*
:string
instead of(:pointer :character)
.
:function
,:argument
, and:struct-slot
. Each of these three keywords represents a context where one can specify:
type char *
:function
, which specifies function return values:argument
, which specifies function arguments:struct-slot
, which specifies structure slots:string
instead of(:pointer :character)
in the corresponding C context.
(:pointer :character)
become:string
allows you to use a Lisp string in a context where a foreign pointer would otherwise be necessary.
nil
.
int vowel_count (char * word);
(def-foreign-function (vowel_count (:name "vowel_count") (:return-type :signed-32bit)) (word (:pointer : character)))
*pointer-to-character-becomes-string*
is'(:argument)
instead ofnil
, the result is:
(def-foreign-function (vowel_count (:name "vowel_count") (:return-type :signed-32bit)) (word :string))
*cpp-arguments*
,*cpp-command*
, *make-linker-name*
,*prefer-multidimensional-arrays*
*default-archive-directories
*
*default-archive-directories*
contains a list of directories that are searched by the functionsload-foreign-files
andload-foreign-libraries
for library(.a)
files. When a library name, such as-lc
, is specified, the full library name is formed by removing-l
from the name, prepending the directory name, and appending the suffix.a
.
> (pprint *default-archive-directories*) ("/usr/lib/lib" "/opt/SUNWspro/SC2.0.1/lib" "/opt/SUNWspro/SC2.0/lib" "/usr/ccs/lib/lib" "/usr/ucblib/lib" "/usr/local/lib/lib")*ansi-c* Variable;; Note that /opt/SUNWspro/SC2.0/libm.a is a symbolic link to ;; ./cg89/libm.a > (let ((*default-archive-directories* '("/usr/lib/lib" "/opt/SUNWspro/SC2.0/lib" "/optf77/SUNWspro/SC2.0.1/lib"))) (load-foreign-files '("ftnf.o" "ftng.o") '("-lM77" "-lF77" "-lV77" "-lm" "-lc"))) ;;; Loading foreign object file "ftnf.o" ;;; Loading foreign object file "ftng.o" ;;; Reading library file "/optf77/SUNWspro/SC2.0.1/libM77.a" ;;; Reading library file "/optf77/SUNWspro/SC2.0.1/libF77.a" ;;; Reading library file "/optf77/SUNWspro/SC2.0.1/libV77.a" ;;; Reading library file "/opt/SUNWspro/SC2.0/cg89/libm.a" ;;; Reading library file "/usr/lib/libc.a" T
*ansi-c*
*ansi-c*
the valuet
(the default) if you are using an ANSI C compiler; otherwise, usenil
. Liquid Common Lisp needs to know whether you are using an ANSI C compiler or not, as some non-ANSI compilers are known to have problems with single floats. Using this variable will avoid those problems.
translate-c-file
Generated with Harlequin WebMaker