[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
FFI
- To: clisp-list@ma2s2.mathematik.uni-karlsruhe.de
- Subject: FFI
- From: Jacques Henke <jack@andromeda.ubis.de>
- Date: Wed, 12 Jun 1996 13:58:34 +0200
>>From sysc.pdx.edu!marcus@tmpmbx.netmbx.de Wed Jun 12 11:12 MET 1996
>Return-Path: <sysc.pdx.edu!marcus@tmpmbx.netmbx.de>
>>Received: by tmpmbx.netmbx.de (/\==/\ Smail3.1.28.1 #28.6)
> id <m0uTlUo-00006iC>; Wed, 12 Jun 96 10:41 MES
>Date: Wed, 12 Jun 1996 00:54:02 -0700
>From: Marcus Daniels <marcus@sysc.pdx.edu>
>To: jack@andromeda.ubis.de
>Subject: FFI
>Reply-To: marcus@sysc.pdx.edu
>Content-Length: 22861
>
>
> The Foreign Function Call Facility
> ==================================
>
>A foreign function description is written as a Lisp file,
>and when compiled it produces a .c file which is then compiled
>by the C compiler and may be linked together with lisp.a.
>
>All symbols relating to the foreign function interface are exported from
>the package FFI. To use them, (USE-PACKAGE "FFI").
>
>Special FFI forms may appear anywhere in the Lisp file.
>
> Overview
> --------
>
>These are the special FFI forms. We have taken a pragmatic approach:
>the only foreign languages we support for now are C and ANSI C.
>
>(DEF-C-TYPE name <c-type>)
>
>(DEF-C-VAR name {option}*)
> option ::=
> (:name <c-name>)
> | (:type <c-type>)
> | (:read-only <boolean>)
> | (:alloc <allocation>)
>
>(DEF-CALL-OUT name {option}*)
> option ::=
> (:name <c-name>)
> | (:arguments {(arg-name <c-type> [<param-mode> [<allocation>]])}*)
> | (:return-type <c-type> [<allocation>])
> | (:language <language>)
>
>(DEF-CALL-IN name {option}*)
> option ::=
> (:name <c-name>)
> | (:arguments {(arg-name <c-type> [<param-mode> [<allocation>]])}*)
> | (:return-type <c-type> [<allocation>])
> | (:language <language>)
>
>(DEF-C-CALL-OUT name {option}*)
> option ::=
> (:name <c-name>)
> | (:arguments {(arg-name <c-type> [<param-mode> [<allocation>]])}*)
> | (:return-type <c-type> [<allocation>])
>
>(DEF-C-CALL-IN name {option}*)
> option ::=
> (:name <c-name>)
> | (:arguments {(arg-name <c-type> [<param-mode> [<allocation>]])}*)
> | (:return-type <c-type> [<allocation>])
>
>(DEF-C-STRUCT name (<ident> <c-type>)*)
>
>(DEF-C-ENUM <name> {<ident> | (<ident> [<value>])}*)
>
>(ELEMENT c-place {index}*)
>(DEREF c-place)
>(SLOT c-place slot-name)
>(CAST c-place <c-type>)
>
>(TYPEOF c-place)
>(SIZEOF c-place), (SIZEOF <c-type>)
>(BITSIZEOF c-place), (BITSIZEOF <c-type>)
>
>(VALIDP foreign-entity)
>
>name is any Lisp symbol.
>
><c-name> is a string.
>
> (Foreign) C types
> -----------------
>
>Foreign C types are used in the FFI. They are *not* regular Common Lisp
>types or CLOS classes.
>
>A <c-type> is either a predefined C type or the name of a type defined by
>DEF-C-TYPE.
>
>The simple C types are these:
>
> Lisp name Lisp equiv C equiv ILU equiv
> nil NIL void (o)
> boolean (MEMBER NIL T) int BOOLEAN
> character STRING-CHAR char SHORT CHARACTER
> char INTEGER signed char
> uchar INTEGER unsigned char
> short INTEGER short
> ushort INTEGER unsigned short
> int INTEGER int
> uint INTEGER unsigned int
> long INTEGER long
> ulong INTEGER unsigned long
> uint8 (UNSIGNED-BYTE 8) uint8 BYTE
> sint8 (SIGNED-BYTE 8) sint8
> uint16 (UNSIGNED-BYTE 16) uint16 SHORT CARDINAL
> sint16 (SIGNED-BYTE 16) sint16 SHORT INTEGER
> uint32 (UNSIGNED-BYTE 32) uint32 CARDINAL
> sint32 (SIGNED-BYTE 32) sint32 INTEGER
> uint64 (UNSIGNED-BYTE 64) uint64 LONG CARDINAL (*)
> sint64 (SIGNED-BYTE 64) sint64 LONG INTEGER (*)
> single-float SINGLE-FLOAT float
> double-float DOUBLE-FLOAT double
>(o) as a result type only.
>(*) does not work on all platforms.
>
>The predefined C types are:
>
> c-type ::=
> <simple-c-type>
> | C-POINTER
> | C-STRING
> | (C-STRUCT <class> (<ident> <c-type>)*)
> | (C-UNION (<ident> <c-type>)*)
> | (C-ARRAY <c-type> dimensions)
> dimensions ::= number | ({number}*)
> | (C-ARRAY-MAX <c-type> maxdimension)
> maxdimension ::= number
> | (C-FUNCTION {option}*)
> option ::=
> (:arguments {(arg-name <c-type> [<param-mode> [<allocation>]])}*)
> | (:return-type <c-type> [<allocation>])
> | (:language <language>)
> | (C-PTR <c-type>)
> | (C-PTR-NULL <c-type>)
> | (C-ARRAY-PTR <c-type>)
>
>(DEF-C-TYPE name <c-type>)
>makes name a shortcut for <c-type>. Note that <c-type> may already refer
>to name. Forward declarations of types are not possible, however.
>
>The type C-POINTER corresponds to what C calls "void*", an opaque pointer.
>
>The type C-STRING corresponds to what C calls "char*", a zero-terminated
>string. Its Lisp equivalent is a string, without the trailing zero character.
>
>The type (C-STRUCT class (ident1 type1) ... (ident2 type2)) is equivalent to
>what C calls "struct { type1 ident1; ...; type2 ident2; }". Its Lisp
>equivalent is: if class is VECTOR, a simple-vector; if class is LIST, a list;
>if class is a symbol naming a structure or CLOS class: an instance of this
>class, with slots of names ident1,...,ident2.
>
>The type (C-UNION (ident1 type1) ... (ident2 type2)) is equivalent to what C
>calls "union { type1 ident1; ...; type2 ident2; }". Conversion to and from
>Lisp assumes that a value is to be viewed as being of type1.
>
>The type (C-ARRAY type dim1 ... dim2) is equivalent to what C calls
>"type [dim1]...[dim2]". Note that when an array is passed as an argument to
>a function in C, it is actually passed as a pointer; you therefore have to
>write (C-PTR (C-ARRAY ...)) for this argument's type.
>
>The type (C-ARRAY-MAX type maxdim) is equivalent to what C calls
>"type [maxdim]", an array containing up to maxdim elements. The array is
>zero-terminated if it contains less than maxdim elements. Conversion from Lisp
>of an array with more than maxdim elements silently ignores the superfluous
>elements.
>
>The type (C-PTR type) is equivalent to what C calls "type *": a pointer to
>a single item of the given type.
>
>The type (C-PTR-NULL type) is also equivalent to what C calls
>"type *": a pointer to a single item of the given type. C-PTR-NULL
>implicits converts NIL into NULL.
>
>The type (C-ARRAY-PTR type) is equivalent to what C calls "type (*)[]":
>a pointer to a zero-terminated array of items of the given type.
>
>The type (C-FUNCTION (:return-type rtype) (:arguments (arg1 type1 ...) ...))
>designates a C function that can be called according to the given prototype
>(rtype (*) (type1, ...)).
>The <language> is either :C (denotes K&R C) or :STDC (denotes ANSI C). It
>specifies whether the C function has been compiled by a K&R C compiler or by
>an ANSI C compiler.
>Conversion between C functions and Lisp functions is transparent.
>
>(DEF-C-STRUCT <name> (<ident> <c-type>)*) defines <name> to be both a
>DEFSTRUCT structure type and a foreign C type with the given slots.
>
>(DEF-C-ENUM <name> {<ident> | (<ident> [<value>])}*) defines <ident>s as
>constants, similarly to the C declaration enum { <ident> [= <value>], ... };
>
>The form (SIZEOF <c-type>) returns the size and alignment of a C type,
>measured in bytes.
>
>The form (BITSIZEOF <c-type>) returns the size and alignment of a C type,
>measured in bits.
>
>The predicate (VALIDP foreign-entity) returns NIL if the foreign-entity
>(e.g. the Lisp equivalent of a C-POINTER) refers to a pointer which is
>invalid because it comes from a previous Lisp session. It returns T if
>foreign-entity can be used within the current Lisp process.
>
> Foreign variables
> -----------------
>
>Foreign variables are variables whose storage is allocated in the foreign
>language module. They can nevertheless be evaluated and modified through SETQ,
>just as normal variables can, except that the range of allowed values is
>limited according to the variable's foreign type. Note that for a foreign
>variable X the form (EQL X X) is not necessarily true, since every time X is
>evaluated its foreign value is converted to a freshly created Lisp value.
>
>(DEF-C-VAR name {option}*)
> option ::=
> (:name <c-name>)
> | (:type <c-type>)
> | (:read-only <boolean>)
> | (:alloc <allocation>)
>
>defines a foreign variable. `name' is the Lisp name, a regular Lisp symbol.
>
>The :name option specifies the name, as seen from C, as a string. If not
>specified, it is derived from the print name of the Lisp name.
>
>The :type option specifies the variable's foreign type.
>
>If the :read-only option is specified and non-NIL, it will be impossible
>to change the variable's value from within Lisp (using SETQ or similar).
>
>The :alloc option can be either :NONE or :MALLOC-FREE and defaults to
>:NONE. If it is :MALLOC-FREE, any values of type C-STRING,
>(C-PTR ...), (C-PTR-NULL ...), (C-ARRAY-PTR ...) within the foreign
>value are assumed to be pointers to malloc()-allocated storage, and
>when SETQ replaces an old value by a new one, the old storage is freed
>using free() and the new storage allocated using malloc(). If it is
>:NONE, SETQ assumes that the pointers point to good storage (not
>NULL!) and overwrites the old values by the new ones. This is
>dangerous (just think of overwriting a string with a longer one or
>storing some data in a NULL pointer...) and deprecated.
>
> Operations on foreign places
> ----------------------------
>
>A foreign variable `name' defined by DEF-C-VAR defines a "place", i.e.
>a form which can also be used as argument to SETF. (An "lvalue" in C
>terminology.) The following operations are available on foreign places:
>
>(ELEMENT place index1 ... indexn)
>Array element: If place is of foreign type (C-ARRAY <c-type> dim1 ... dimn)
>and 0 <= index1 < dim1, ..., 0 <= indexn < dimn, this will be the place
>corresponding to (aref place index1 ... indexn) or place[index1]...[indexn].
>It is a place of type <c-type>.
>If place is of foreign type (C-ARRAY-MAX <c-type> dim) and 0 <= index < dim,
>this will be the place corresponding to (aref place index) or place[index].
>It is a place of type <c-type>.
>
>(DEREF place) Dereference pointer: If place is of foreign type
>(C-PTR <c-type>) or (C-PTR-NULL <c-type>) this will be the place the
>pointer points to. It is a place of type <c-type>.
>
>(SLOT place slot-name)
>Struct or union component: If place is of foreign type
>(C-STRUCT <class> ... (slot-name <c-type>) ...) or of type
>(C-UNION ... (slot-name <c-type>) ...), this will be of type <c-type>.
>
>(CAST place <c-type>)
>Type change: A place denoting the same memory locations as the original place,
>but of type <c-type>.
>
>(TYPEOF place)
>returns the <c-type> corresponding to the place.
>
>(SIZEOF place) returns the size and alignment of the C type of place,
>measured in bytes.
>
>(BITSIZEOF place) returns the size and alignment of the C type of place,
>measured in bits.
>
> Foreign functions
> -----------------
>
>Foreign functions are functions which are defined in the foreign language.
>There are named foreign functions (imported via DEF-CALL-OUT or created via
>DEF-CALL-IN) and anonymous foreign functions; they arise through conversion
>of function pointers.
>
>A "call-out" function is a foreign function called from Lisp: control flow
>temporarily leaves Lisp.
>A "call-in" function is a Lisp function called from the foreign language:
>control flow temporary enters Lisp.
>
>(DEF-CALL-OUT name {option}*)
> option ::=
> (:name <c-name>)
> | (:arguments {(arg-name <c-type> [<param-mode> [<allocation>]])}*)
> | (:return-type <c-type> [<allocation>])
> | (:language <language>)
>
>defines a named call-out function. Any Lisp function call to #'name is
>redirected to call the C function <c-name>.
>
>DEF-C-CALL-OUT is equivalent to DEF-CALL-OUT with :LANGUAGE :C.
>
>(DEF-CALL-IN name {option}*)
> option ::=
> (:name <c-name>)
> | (:arguments {(arg-name <c-type> [<param-mode> [<allocation>]])}*)
> | (:return-type <c-type> [<allocation>])
> | (:language <language>)
>
>defines a named call-in function. Any C function call to the C function
><c-name> is redirected to call the Lisp function #'name.
>
>DEF-C-CALL-IN is equivalent to DEF-CALL-IN with :LANGUAGE :C.
>
> Argument and result passing conventions
> ---------------------------------------
>
>When passed to and from functions, allocation of arguments and results is
>handled as follows:
>
>Values of <simple-c-type>, C-POINTER are passed on the stack, with dynamic
>extent. The <allocation> is effectively ignored.
>
>Values of type C-STRING, (C-PTR ...), (C-PTR-NULL ...), (C-ARRAY-PTR ...)
>need storage. The <allocation> specifies the allocation policy:
> <allocation> is :NONE means that no storage is allocated.
> <allocation> is :ALLOCA means allocation of storage on the stack,
> which has dynamic extent.
> <allocation> is :MALLOC-FREE means that storage will be allocated
> via malloc() and freed via free().
>If no <allocation> is specified, the default <allocation> is :NONE for most
>types, but :ALLOCA for C-STRING, (C-PTR ...), (C-PTR-NULL ...), and
> (C-ARRAY-PTR ...) and for :OUT arguments. [Subject to change!]
>The :MALLOC-FREE policy provides the ability to pass arbitrarily nested
>structs containing pointers pointing to structs ... within a single conversion.
>
>For call-out functions:
> For arguments passed from Lisp to C:
> If <allocation> is :MALLOC-FREE,
> Lisp allocates the storage using malloc() and never deallocates it.
> The C function is supposed to call free() when done with it.
> If <allocation> is :ALLOCA,
> Lisp allocates the storage on the stack, with dynamic extent. It is
> freed when the C function returns.
> If <allocation> is :NONE,
> Lisp assumes that the pointer already points to a valid area of the
> proper size and puts the result value there. This is dangerous! and
> deprecated.
> For results passed from C to Lisp:
> If <allocation> is :MALLOC-FREE,
> Lisp calls free() on it when done.
> If <allocation> is :NONE,
> Lisp does nothing.
>For call-in functions:
> For arguments passed from C to Lisp:
> If <allocation> is :MALLOC-FREE,
> Lisp calls free() on it when done.
> If <allocation> is :ALLOCA or :NONE,
> Lisp does nothing.
> For results passed from Lisp to C:
> If <allocation> is :MALLOC-FREE,
> Lisp allocates the storage using malloc() and never deallocates it.
> The C function is supposed to call free() when done with it.
> If <allocation> is :NONE,
> Lisp assumes that the pointer already points to a valid area of the
> proper size and puts the result value there. This is dangerous! and
> deprecated.
>
>A function parameter's <param-mode> may be
>either :IN (means: read-only):
> The caller passes information to the callee.
>or :OUT (means: write-only):
> The callee passes information back to the caller on return.
> When viewed as a Lisp function, there is no Lisp argument corresponding
> to this, instead it means an additional return value.
>or :IN-OUT (means: read-write):
> Information is passed from the caller to the callee and then back to
> the caller. When viewed as a Lisp function, the ":OUT" value is
> returned as an additional multiple value.
>The default is :IN.
>
>[Currently, only :IN is fully implemented. :OUT works only with
><allocation> = :ALLOCA.]
>
>On AmigaOS, <allocation> may not be :MALLOC-FREE because there is no commonly
>used malloc()/free() library function.
>
>On AmigaOS, the <allocation> may be followed by a register specification,
>any of the symbols :D0, :D1, :D2, :D3, :D4, :D5, :D6, :D7, :A0, :A1, :A2,
>:A3, :A4, :A5, :A6, each representing one 680x0 register. This works only
>for integral types: integers, pointers, C-STRING, C-FUNCTION.
>
>Passing C-STRUCT, C-UNION, C-ARRAY, C-ARRAY-MAX values as arguments (not via
>pointers) is only possible to the extent the C compiler supports it. Most C
>compilers do it right, but some C compilers (such as gcc on hppa) have
>problems with this.
>
> Examples
> --------
>
>Ex. 1: The C declaration
>
> struct foo {
> int a;
> struct foo * b[100];
> };
>
>corresponds to
>
> (def-c-struct foo
> (a int)
> (b (c-array (c-ptr foo) 100))
> )
>
>The element access
>
> struct foo f;
> f.b[7].a
>
>corresponds to
>
> (declare (type foo f))
> (foo-a (aref (foo-b f) 7)) or (slot-value (aref (slot-value f 'b) 7) 'a)
>
>Ex. 2: Here is an example of an external C variable and some accesses:
>
> struct bar {
> short x, y;
> char a, b;
> int z;
> struct bar * n;
> };
>
> extern struct bar * my_struct;
>
> my_struct->x++;
> my_struct->a = 5;
> my_struct = my_struct->n;
>
>corresponds to
>
> (def-c-struct bar
> (x short)
> (y short)
> (a char)
> (b char) ; or (b character) if it represents a character, not a number
> (z int)
> (n (c-ptr bar))
> )
>
> (def-c-var my_struct (:type (c-ptr bar)))
>
> (setq my_struct (let ((s my_struct)) (incf (slot-value s 'x)) s))
> or (incf (slot my_struct 'x))
> (setq my_struct (let ((s my_struct)) (setf (slot-value s 'a) 5) s))
> or (setf (slot my_struct 'a) 5)
> (setq my_struct (slot-value my_struct 'n))
> or (setq my_struct (deref (slot my_struct 'n)))
>
>Ex. 3: An example for calling an external function:
>On ANSI C systems, <stdlib.h> contains the declarations
>
> typedef struct {
> int quot; /* Quotient */
> int rem; /* Remainder */
> } div_t;
> extern div_t div (int numer, int denom);
>
>This translates to
>
> (def-c-struct div_t
> (quot int)
> (rem int)
> )
> (def-c-call-out div (:arguments (numer int) (denom int))
> (:return-type div_t)
> )
>
>Sample call from within Lisp:
>
> > (div 20 3)
> #S(DIV :QUOT 6 :REM 2)
>
>Ex. 4: Another example for calling an external function:
>
>Suppose the following is defined in a file "cfun.c":
>
> struct cfunr { int x; char *s; };
> struct cfunr * cfun (i,s,r,a)
> int i;
> char *s;
> struct cfunr * r;
> int a[10];
> {
> int j;
> struct cfunr * r2;
> printf("i = %d\n", i);
> printf("s = %s\n", s);
> printf("r->x = %d\n", r->x);
> printf("r->s = %s\n", r->s);
> for (j = 0; j < 10; j++) printf("a[%d] = %d.\n", j, a[j]);
> r2 = (struct cfunr *) malloc (sizeof (struct cfunr));
> r2->x = i+5;
> r2->s = "A C string";
> return r2;
> }
>
>It is possible to call this function from Lisp using the file "callcfun.lsp"
>(don't call it "cfun.lsp" - COMPILE-FILE would overwrite "cfun.c") whose
>contents is:
>
> (in-package "TEST-C-CALL" :use '("LISP" "FFI"))
> (def-c-struct cfunr (x int) (s c-string))
> (def-c-call-out cfun (:arguments (i int)
> (s c-string)
> (r (c-ptr cfunr) :in :alloca)
> (a (c-ptr (c-array int 10)) :in
:alloca)
> )
> (:return-type (c-ptr cfunr))
> )
> (defun call-cfun ()
> (cfun 5 "A Lisp string" (make-cfunr :x 10 :s "Another Lisp string")
> '#(0 1 2 3 4 5 6 7 8 9)
> ) )
>
>Use the module facility:
>
> $ clisp-link create-module-set cfun callcfun.c
> $ cc -O -c cfun.c
> $ cd cfun
> $ ln -s ../cfun.o cfun.o
> Add cfun.o to NEW_LIBS and NEW_FILES in link.sh.
> $ cd ..
> $ base/lisp.run -M base/lispinit.mem -c callcfun.lsp
> $ clisp-link add-module-set cfun base base+cfun
> $ base+cfun/lisp.run -M base+cfun/lispinit.mem -i callcfun
> > (test-c-call::call-cfun)
> i = 5
> s = A Lisp string
> r->x = 10
> r->s = Another Lisp string
> a[0] = 0.
> a[1] = 1.
> a[2] = 2.
> a[3] = 3.
> a[4] = 4.
> a[5] = 5.
> a[6] = 6.
> a[7] = 7.
> a[8] = 8.
> a[9] = 9.
> #S(TEST-C-CALL::CFUNR :X 10 :S "A C string")
> >
> $ rm -r base+cfun
>
>Note that there is a memory leak here: The return value r2 of cfun() is
>malloc()ed but never free()d. Specifying
> (:return-type (c-ptr cfunr) :malloc-free)
>is not an alternative because this would also free(r2->x) but r2->x is a
>pointer to static data.
>
>Ex. 5: To sort an array of double-floats using the Lisp function SORT
>instead of the C library function qsort(), one can use the following
>interface code "sort1.c". The main problem is to pass a variable-sized array.
>
> extern void lispsort_begin (int);
> void* lispsort_function;
> void lispsort_double (int n, double * array)
> {
> double * sorted_array;
> int i;
> lispsort_begin(n); /* store #'sort2 in lispsort_function */
> sorted_array = ((double * (*) (double *)) lispsort_function)
(array);
> for (i = 0; i < n; i++) array[i] = sorted_array[i];
> free(sorted_array);
> }
>
>This is accompanied by "sort2.lsp":
>
> (use-package "FFI")
> (def-call-in lispsort_begin (:arguments (n int))
> (:return-type nil)
> (:language :stdc)
> )
> (def-c-var lispsort_function (:type c-pointer))
> (defun lispsort_begin (n)
> (setf (cast lispsort_function
> `(c-function
> (:arguments (v (c-ptr (c-array double-float ,n))))
> (:return-type (c-ptr (c-array double-float ,n))
> :malloc-free
> ) )
> )
> #'sort2
> ) )
> (defun sort2 (v)
> (declare (type vector v))
> (sort v #'<)
> )
>
>To test this, use the following test file "sorttest.lsp":
>
> (def-call-out sort10
> (:name "lispsort_double")
> (:language :stdc)
> (:arguments (n int)
> (array (c-ptr (c-array double-float 10))
> :in-out
> ) ) )
>
>Now try
>
> $ clisp-link create-module-set sort sort2.c sorttest.c
> $ cc -O -c sort1.c
> $ cd sort
> $ ln -s ../sort1.o sort1.o
> Add sort1.o to NEW_LIBS and NEW_FILES in link.sh.
> $ cd ..
> $ base/lisp.run -M base/lispinit.mem -c sort2.lsp sorttest.lsp
> $ clisp-link add-module-set sort base base+sort
> $ base+sort/lisp.run -M base+sort/lispinit.mem -i sort2 sorttest
> > (sort10 10 '#(0.501d0 0.528d0 0.615d0 0.550d0 0.711d0
> 0.523d0 0.585d0 0.670d0 0.271d0 0.063d0))
> #(0.063d0 0.271d0 0.501d0 0.523d0 0.528d0 0.55d0 0.585d0 0.615d0
0.67d0 0.711d0)
> $ rm -r base+sort
>
>
>
>
-----------------------------------------------------------------
Unternehmensberatung fuer integrierte Systeme
UBIS GmbH
Jacques Henke
Alt-Moabit 98
D-10559 Berlin
tel: +49 30 399 29 - 753
fax: +49 30 399 29 - 900
e-mail: jack@ubis.de
www: http://www.ubis.de
-----------------------------------------------------------------