Previous: Foreign Pointers, Up: Foreign Function Interface [Contents][Index]
Of course, the land of C is not all nouns and no verbs: there are functions too, and Guile allows you to call them.
Make a foreign function.
Given the foreign void pointer func_ptr, its argument and return types arg_types and return_type, return a procedure that will pass arguments to the foreign function and return appropriate values.
arg_types should be a list of foreign types.
return_type
should be a foreign type. See Foreign Types, for
more information on foreign types.
If return-errno? is true, or when calling
scm_pointer_to_procedure_with_errno
, the returned procedure will
return two values, with errno
as the second value.
Here is a better definition of (math bessel)
:
(define-module (math bessel) #:use-module (system foreign) #:export (j0)) (define libm (dynamic-link "libm")) (define j0 (pointer->procedure double (dynamic-func "j0" libm) (list double)))
That’s it! No C at all.
Numeric arguments and return values from foreign functions are
represented as Scheme values. For example, j0
in the above
example takes a Scheme number as its argument, and returns a Scheme
number.
Pointers may be passed to and returned from foreign functions as well.
In that case the type of the argument or return value should be the
symbol *
, indicating a pointer. For example, the following
code makes memcpy
available to Scheme:
(define memcpy (let ((this (dynamic-link))) (pointer->procedure '* (dynamic-func "memcpy" this) (list '* '* size_t))))
To invoke memcpy
, one must pass it foreign pointers:
(use-modules (rnrs bytevectors)) (define src-bits (u8-list->bytevector '(0 1 2 3 4 5 6 7))) (define src (bytevector->pointer src-bits)) (define dest (bytevector->pointer (make-bytevector 16 0))) (memcpy dest src (bytevector-length src-bits)) (bytevector->u8-list (pointer->bytevector dest 16)) ⇒ (0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0)
One may also pass structs as values, passing structs as foreign pointers. See Foreign Structs, for more information on how to express struct types and struct values.
“Out” arguments are passed as foreign pointers. The memory pointed to by the foreign pointer is mutated in place.
;; struct timeval { ;; time_t tv_sec; /* seconds */ ;; suseconds_t tv_usec; /* microseconds */ ;; }; ;; assuming fields are of type "long" (define gettimeofday (let ((f (pointer->procedure int (dynamic-func "gettimeofday" (dynamic-link)) (list '* '*))) (tv-type (list long long))) (lambda () (let* ((timeval (make-c-struct tv-type (list 0 0))) (ret (f timeval %null-pointer))) (if (zero? ret) (apply values (parse-c-struct timeval tv-type)) (error "gettimeofday returned an error" ret)))))) (gettimeofday) ⇒ 1270587589 ⇒ 499553
As you can see, this interface to foreign functions is at a very low, somewhat dangerous level21.
The FFI can also work in the opposite direction: making Scheme procedures callable from C. This makes it possible to use Scheme procedures as “callbacks” expected by C function.
Return a pointer to a C function of type return-type taking arguments of types arg-types (a list) and behaving as a proxy to procedure proc. Thus proc’s arity, supported argument types, and return type should match return-type and arg-types.
As an example, here’s how the C library’s qsort
array sorting
function can be made accessible to Scheme (see qsort
in The GNU C Library Reference Manual):
(define qsort! (let ((qsort (pointer->procedure void (dynamic-func "qsort" (dynamic-link)) (list '* size_t size_t '*)))) (lambda (bv compare) ;; Sort bytevector BV in-place according to comparison ;; procedure COMPARE. (let ((ptr (procedure->pointer int (lambda (x y) ;; X and Y are pointers so, ;; for convenience, dereference ;; them before calling COMPARE. (compare (dereference-uint8* x) (dereference-uint8* y))) (list '* '*)))) (qsort (bytevector->pointer bv) (bytevector-length bv) 1 ;; we're sorting bytes ptr))))) (define (dereference-uint8* ptr) ;; Helper function: dereference the byte pointed to by PTR. (let ((b (pointer->bytevector ptr 1))) (bytevector-u8-ref b 0))) (define bv ;; An unsorted array of bytes. (u8-list->bytevector '(7 1 127 3 5 4 77 2 9 0))) ;; Sort BV. (qsort! bv (lambda (x y) (- x y))) ;; Let's see what the sorted array looks like: (bytevector->u8-list bv) ⇒ (0 1 2 3 4 5 7 9 77 127)
And voilà!
Note that procedure->pointer
is not supported (and not defined)
on a few exotic architectures. Thus, user code may need to check
(defined? 'procedure->pointer)
. Nevertheless, it is available on
many architectures, including (as of libffi 3.0.9) x86, ia64, SPARC,
PowerPC, ARM, and MIPS, to name a few.
Previous: Foreign Pointers, Up: Foreign Function Interface [Contents][Index]