PFFI is a portable foreign function interface for R6RS Scheme implementations.
Suppose we have the following C file and will be compiled to libfoo.so
int add(int a, int b)
{
return a + b;
}
Now we want to use the above shared object from Scheme.
#!r6rs
(import (rnrs) (pffi))
(define shared-object (open-shared-object "libfoo.so"))
(define foo (foreign-procedure shared-object int add (int int)))
(foo 1 2) ;; => 3
See examples/
directory for more examples.
This layer of the APIs wraps implementations specific foreign object accessing.
Returns shared object.
Lookup foreign procedure symbol-name from given shared-object and returns foreign procedure. A foreign procedure is a mere procedure so users can just call as if it's a Scheme procedure.
If the second form is used, then conv must be implementation specific
calling conventions. For example __cdecl
for Chez Scheme.
Creates a callback. Callback is a mechanism that makes foreign procedure call Scheme procedure. The given proc is the procedure called from foreign procedure.
Release allocated callback if needed.
Callback object may not be released automatically so it is user's responsibilty to make sure to release it.
Lookup foreign variable symbol-name from given shared-object and binds it to scheme-name. If scheme-name is not given, then it is generated from symbol-name with following rules:
- converting to lower case
- converting
_
to-
type must be specified properly. Currently type can only be numerical.
The bound variable is settable, thus set!
syntax can change the value
if it's allowed.
Implementations may have own bindings for foreign types. This layer absorbs the difference. Currently following types are supported.
char
unsigned-char
short
unsigned-short
int
unsigned-int
long
unsigned-long
float
double
int8_t
uint8_t
int16_t
uint16_t
int32_t
uint32_t
int64_t
uint64_t
pointer
void
callback
Above types are all variable except callback
. Callback is a procedure
which is called from foreign world to Scheme world. Thus it may need to
have foreign types.
C's variadic arguments (i.e. argument specified by ...
) can be written by
specifying ___
. For example, suppose we have sum
C function which takes
an int as the number of the variadic arguments, and variadic arguments.
To specify this, you can write like this
(foreign-procedure lib int sum (int ___))
The ___
must be the last and must not appear more than once.
Returns #t if given o is a pointer object.
Returns #t if given pointer value is 0.
Converts given bytevector bv to implementation dependent pointer object.
Converts given pointer to bytevector whose length is len and elements are derefered values of the pointer p.
Converts given integer i to a pointer object. The given integer i is the address of returning pointer.
Converts given pointer p to an integer. The returning integer represents the address of the pointer p.
${type} must be one of the following types:
uint8
int8
uint16
int16
uint32
int32
uint64
int64
unsigned-char
char
unsigned-short
short
unsigned-int
int
unsigned-long
long
float
double
pointer
Returns corresponding type value form give pointer p. The offset is byte offset of the given p not aligned value.
${type} must be the same as pointer-ref-c-${type}
.
Sets given value which is converted to corresponding type to pointer p on offset location. offset is byte offset of the given p.
Defines a structure. The macro creates constructor, predicate, size-of variable and accessors.
ctr is the constructor which returns newly allocated bytevector whose size is the size of this struct.
pred is the predicate, which simply check if the givn object is a bytevector and it has enough size for this structure. It doesn't distinguish 2 bytevectors created by 2 different ways as long as it has enough size.
Size-of variable is created adding size-of-
prefix to name. This
variable contains the size of this structure.
spec can be one of the followings:
- (
fields
field spec ...) - (
protocol
proc) - (
parent
parent-structure) - (
alignment
alignment)
The same clause can only appear once. If there are more than one the same
clause, it raises &syntax
.
field spec can be one the followings:
- (
fields
(type field)) - (
fields
(type field getter)) - (
fields
(type field getter setter))
type must be a type listed in Foreign types section except callback
.
field is the field name. This is used for generating getter and setter. In other words, it doesn't have to be meaningful name as long as getter and setter is specified.
getter is an accessor to retrieve the structure field value. If this is not
specified, then it is created by adding _name_-
prefix to field.
setter is an accessor to set the structure field value. If this is not
specified, then it is created by adding _name_-
prefix and -set!
suffix
to field.
proc is a procedure which is the same usage as define-record-type
's one.
parent-structure must be a foreign structure defined by this macro. There is no actual hierarchy but just putting specified structure in front of this structure so that it seems it has a hierarchy. For example:
alignment must be an integer or integer variable of 1
, 2
, 4
, 8
or 16
. This specifies the struct alignment size. This is equivalent of
#pragma pack(n)
.
(define-foreign-struct p
(fields (int count)))
(define-foreign-struct c
(fields (pointer elements))
(parent p))
(make-c 0 (integer->pointer 0))
;; 32 bits -> #vu8(0 0 0 0 0 0 0 0)
;; 64 bits -> #vu8(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
is the same as the following
(define-foreign-struct p
(fields (int count)))
(define-foreign-struct c
(fields (p p)
(pointer elements)))
(make-c (make-p 0) (integer->pointer 0))
;; 32 bits -> #vu8(0 0 0 0 0 0 0 0)
;; 64 bits -> #vu8(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
If the first form is used, then ctr and pred are created by adding make-
prefix and ?
suffix respectively, like define-record-type
.
Defines a union structure. The macro creates constructor, predicate, size-of
variable and accessors. The auto generating convension is the same as
define-foreign-struct
unless its specified.
The spec can be one of the followings:
- (
fields
field spec ...) - (
protocol
proc)
The fields
is the same as define-foreign-struct
.
The proc of protocol
should look like this:
(lambda (p)
(lamba (f)
(p 'f f)))
The p takes 0 or 2 arguments. If the first form is used, then it creates 0 padded bytevector. If the second form is used, then it searches the field setter named f and sets the value f. If it's not found, then it behave as if nothing is passed.
- Sagittarius (0.9.8 or later)
- Racket (plt-r6rs v8.3)
- Guile (3.0.8)
- Chez Scheme (v9.5)
The below implementations are no loger supported due to the inactiveness or officially declared to be archived.
Larceny (v0.98)Vicare (0.3d7)Mosh (0.2.7)
Vicare doesn't support bytevector to pointer convertion whom converted pointer is shared with source bytevector. So this behaviour is emulated on this library. This emulation doesn't work on NULL pointer. So the following situation doesn't work: Suppose a shared object set a pointer value to NULL, then initialise it on a function. Scheme code first loads the pointer, then call the initialisation function, however the loaded pointer still indicates NULL.
On Larceny, GC may move pointers so converting bytevector uses wrapper technique the same as Vicare. Thus the same limitation is applied to it.
The latest released version of Ypsilon has very limited FFI interface.
The biggest problem is c-function
is defined as a macro which I think
very limited.
Trunk repository version has far more APIs but it's not released nor maintained. Thus it is hard for me to make portable layer for it.
.Net makes a bit things harder. And its FFI support is very limited. (e.g. it doesn't work on Mono)
To support above non supported implementations, your pull request is the fastest way :)