508 lines
19 KiB
Markdown
508 lines
19 KiB
Markdown
---
|
|
title: Portable Foreign Function Interface for R7RS Documentation
|
|
version: 0.6.0
|
|
---
|
|
|
|
# Portable Foreign Function Interface for R7RS
|
|
|
|
Portable foreign function interface for R7RS. It is portable in the sense that
|
|
it supports multiple implementations, as opposed to being portable by
|
|
conforming to some specification.
|
|
|
|
[Project](https://todo.sr.ht/~retropikzel/r7rs-pffi)
|
|
|
|
[Issue trackers](https://sr.ht/~retropikzel/r7rs-pffi/trackers)
|
|
|
|
[Maling lists](https://sr.ht/~retropikzel/r7rs-pffi/lists)
|
|
|
|
[Jenkins](https://jenkins.scheme.org/job/r7rs_pffi/job/r7rs-pffi/)
|
|
|
|
## Table of contents
|
|
|
|
<nav>
|
|
|
|
- [Goals](#goals)
|
|
- [Non Goals](#non-goals)
|
|
- [Status](#status)
|
|
- [Current caveats](#current-caveats)
|
|
- [Roadmap](#roadmap)
|
|
- [Feature implementation table](#feature-implementation-table)
|
|
- [Primitives](#feature-implementation-table-primitives)
|
|
- [Built upon](#feature-implementation-table-built-upon)
|
|
- [Documentation](#documentation)
|
|
- [Installation](#installation)
|
|
- [Compiling the library](#compiling-the-library)
|
|
- [Chibi](#compiling-the-library-chibi)
|
|
- [Gauche](#compiling-the-library-gauche)
|
|
- [Dependencies](#dependencies)
|
|
- [Chibi](#dependencies-chibi)
|
|
- [Chicken](#dependencies-chicken)
|
|
- [Racket](#dependencies-racket)
|
|
- [Kawa](#dependencies-kawa)
|
|
- [Reference](#reference)
|
|
- [Types](#types)
|
|
- [Procedures and macros](#procedures-and-macros)
|
|
- [pffi-init](#pffi-init)
|
|
- [pffi-size-of](#pffi-size-of)
|
|
- [pffi-align-of](#pffi-align-of)
|
|
- [pffi-define-library](#pffi-define-library)
|
|
- [pffi-pointer-null](#pffi-pointer-null)
|
|
- [pffi-pointer-null?](#pffi-pointer-null?)
|
|
- [pffi-pointer-allocate](#pffi-pointer-allocate)
|
|
- [pffi-pointer-address](#pffi-pointer-address)
|
|
- [pffi-pointer?](#pffi-pointer?)
|
|
- [pffi-pointer-free](#pffi-pointer-free)
|
|
- [pffi-pointer-set!](#pffi-pointer-set!)
|
|
- [pffi-pointer-get](#pffi-pointer-get)
|
|
- [pffi-string->pointer](#pffi-string-to-pointer)
|
|
- [pffi-pointer->string](#pffi-pointer-to-string)
|
|
- [pffi-struct-make](#pffi-struct-make)
|
|
- [pffi-struct-pointer](#pffi-struct-pointer)
|
|
- [pffi-struct-offset-get](#pffi-struct-offset-get)
|
|
- [pffi-struct-get](#pffi-struct-get)
|
|
- [pffi-struct-set!](#pffi-struct-set!)
|
|
- [pffi-list->array](#pffi-list-to-array)
|
|
- [pffi-array->list](#pffi-array-to-list)
|
|
- [pffi-define](#pffi-define)
|
|
- [pffi-define-callback](#pffi-define-callback)
|
|
|
|
</nav>
|
|
|
|
<main>
|
|
|
|
## Goals
|
|
<a name="goals"></a>
|
|
|
|
- Support only R7RS implementations
|
|
- Same interface on all implementations
|
|
- Some things that are procedures on one implementation are macros on other,
|
|
but they must behave the same
|
|
- Stability and being boring after 1.0.0 is reached
|
|
|
|
## Non goals
|
|
<a name="non-goals"></a>
|
|
|
|
- To have every possible FFI feature
|
|
- Compiling of used library C code at any point
|
|
- That is no stubs, no C code generated by the library and so on
|
|
- The pffi library itself may require compilation on installation
|
|
|
|
## Status
|
|
<a name="status"></a>
|
|
|
|
Currently the interface of the library is in okay shape. It propably will not change much but no
|
|
guarantees are being made just yet.
|
|
|
|
### Current caveats
|
|
<a name="current-caveats"></a>
|
|
|
|
- No way to pass structs by value
|
|
- Most implementations are missing callback support
|
|
- Always pass arguments to pffi functions/macros as '(1 2 3) and not (list 1 2 3)
|
|
- Always pass pffi-define-callback procedure as lambda in place
|
|
- No support for variadic function arguments
|
|
- Can be partially worked around by defining multiple versions of same
|
|
function with different amount of arguments
|
|
|
|
## Roadmap
|
|
|
|
For roadmap to 1.0.0 see [issues](https://todo.sr.ht/~retropikzel/r7rs-pffi?search=status%3Aopen%20label%3A%221.0.0%22)
|
|
|
|
## Feature mplementation table
|
|
<a name="feature-implementation-table"></a>
|
|
|
|
## Primitives
|
|
<a name="feature-implementation-table-primitives"></a>
|
|
|
|
| | pffi-init | pffi-size-of | pffi-define-library | pffi-pointer-null | pffi-pointer-null? | pffi-pointer-address | pffi-pointer? | pffi-pointer-set! | pffi-pointer-get | pffi-define | pffi-define-callback |
|
|
|--------------|:---------:|:------------:|:-------------------:|:-----------------:|:------------------:|:--------------------:|:-------------:|:-----------------:|:----------------:|:-----------:|:--------------------:|
|
|
| Chibi | X | X | X | X | X | X | X | X | X | X | |
|
|
| Chicken-5 | X | X | X | X | X | X | X | X | X | X | X |
|
|
| Cyclone | X | X | X | X | X | | X | X | X | X | |
|
|
| Gambit | X | X | | | | X | | | | | |
|
|
| Gauche | X | X | X | X | X | X | X | X | X | X | |
|
|
| Gerbil | X | | | | | | | | | | |
|
|
| Guile | X | X | X | X | X | X | X | X | X | X | X |
|
|
| Kawa | X | X | X | X | X | X | X | X | X | X | X |
|
|
| Larceny | X | | | | | | | | | | |
|
|
| Mosh | X | X | X | X | X | | X | X | X | X | X |
|
|
| Racket | X | X | X | X | X | X | X | X | X | X | X |
|
|
| Saggittarius | X | X | X | X | X | X | X | X | X | X | X |
|
|
| Skint | X | | | | | | | | | | |
|
|
| Stklos | X | X | X | X | X | | X | | | | |
|
|
| tr7 | | | | | | | | | | | |
|
|
| Ypsilon | X | X | X | X | X | X | X | X | X | X | X |
|
|
|
|
## Built upon
|
|
<a name="feature-implementation-table-built-upon"></a>
|
|
|
|
These features are built upon the primitives and if primitives are implemented
|
|
and work, they should work too.
|
|
|
|
- pffi-pointer-allocate
|
|
- pffi-pointer-free
|
|
- pffi-pointer-\>string
|
|
- pffi-string-\>pointer
|
|
- pffi-struct-make
|
|
- pffi-struct-pointer
|
|
- pffi-struct-offset-get
|
|
- pffi-struct-get
|
|
- pffi-struct-set!
|
|
|
|
### Not started
|
|
<a name="not-started"></a>
|
|
|
|
- [LIPS](https://lips.js.org/)
|
|
- Will work on nodejs by using some C FFI library from npm
|
|
- Javascript side needs design
|
|
- [Biwascheme](https://www.biwascheme.org/)
|
|
- Will work on nodejs by using some C FFI library from npm
|
|
- Javascript side needs design
|
|
- [MIT-Scheme](https://www.gnu.org/software/mit-scheme/)
|
|
- Need to study the implementation more
|
|
- [Airship](https://gitlab.com/mbabich/airship-scheme)
|
|
- Need to study the implementation more
|
|
- [Other gambit targets](https://gambitscheme.org/)
|
|
- Gambit compiles to different targets other than C too, for example Javascript. It would be cool
|
|
and interesting to see if this FFI could also support some of those
|
|
- When LIPS and Biwascheme Javascript side is done then Gambit should be done too
|
|
- [s48-r7rs](https://codeberg.org/prescheme/s48-r7rs)
|
|
- Need to study the implementation more
|
|
- [prescheme](https://codeberg.org/prescheme/prescheme)
|
|
- Need to study the implementation more
|
|
|
|
### Other
|
|
<a name="other"></a>
|
|
|
|
- [s7](https://scheme.fail://ccrma.stanford.edu/software/snd/snd/s7.html)
|
|
- Propably does not need FFI as it is embeddable only
|
|
- [Loko](https://scheme.fail/)
|
|
- Desires no C interop, I can respect that
|
|
|
|
## Documentation <a name="documentation"></a>
|
|
|
|
### Installation <a name="installation"></a>
|
|
|
|
Download the latest release from
|
|
[https://git.sr.ht/~retropikzel/r7rs-pffi/refs](https://git.sr.ht/~retropikzel/r7rs-pffi/refs).
|
|
|
|
Unpack it somewhere and copy the directory called "retropikzel" to your projects
|
|
library directory. For the rest of this documentation it is assumed to be ./snow.
|
|
|
|
#### Compiling the libary <a name="compiling-the-library"></a>
|
|
Some implementations need extra step of compiling the library. Change directory
|
|
to ./snow/retropikzel/pffi and run command corresponding to your implementation.
|
|
|
|
##### Chibi <a name="compiling-the-library-chibi"></a>
|
|
|
|
make -C ./snow/retropikzel/pffi chibi-pffi.so
|
|
|
|
##### Gauche <a name="compiling-the-library-gauche"></a>
|
|
|
|
make -C ./snow/retropikzel/pffi gauche-pffi.so
|
|
|
|
#### Dependencies <a name="dependencies"></a>
|
|
|
|
Some implementations have external dependencies beyond just the library.
|
|
|
|
#### Chibi <a name="dependencies-chibi"></a>
|
|
|
|
Needs libffi-dev, on Debian/Ubuntu/Mint install with:
|
|
|
|
apt install libffi-dev
|
|
|
|
#### Chicken <a name="dependencies-chicken"></a>
|
|
|
|
Needs [r7rs egg](https://wiki.call-cc.org/eggref/5/r7rs), install with:
|
|
|
|
chicken-install r7rs
|
|
|
|
#### Racket <a name="dependencies-racket"></a>
|
|
|
|
Needs [racket-r7rs](https://github.com/lexi-lambda/racket-r7rs), install with:
|
|
|
|
raco pkg install --auto r7rs
|
|
|
|
#### Kawa <a name="dependencies-kawa"></a>
|
|
|
|
Kawa Needs at least Java version 22 and jvm flags:
|
|
|
|
- \--add-exports java.base/jdk.internal.foreign.abi=ALL-UNNAMED
|
|
- \--add-exports java.base/jdk.internal.foreign.layout=ALL-UNNAMED
|
|
- \--add-exports java.base/jdk.internal.foreign=ALL-UNNAMED
|
|
- \--enable-native-access=ALL-UNNAMED
|
|
|
|
## Reference
|
|
<a name="reference"></a>
|
|
|
|
### Types
|
|
<a name="types"></a>
|
|
|
|
Types are given as symbols, for example 'int8 or 'pointer.
|
|
|
|
- int8
|
|
- uint8
|
|
- int16
|
|
- uint16
|
|
- int32
|
|
- uint32
|
|
- int64
|
|
- uint64
|
|
- char
|
|
- unsigned-char
|
|
- short
|
|
- unsigned-short
|
|
- int
|
|
- unsigned-int
|
|
- long
|
|
- unsigned-long
|
|
- float
|
|
- double
|
|
- pointer
|
|
- callback
|
|
- Callback function
|
|
|
|
### Procedures and macros
|
|
<a name="procedures-and-macros"></a>
|
|
|
|
Some of these are procedures and some macros, it might also change implementation to implementation.
|
|
|
|
#### pffi-init <a name="pffi-init"></a>
|
|
|
|
**pffi-init**
|
|
|
|
Always call this first, on most implementation it does nothing but some implementations might need
|
|
initialisation run.
|
|
|
|
#### pffi-size-of <a name="pffi-size-of"></a>
|
|
|
|
**pffi-size-of** object -> number
|
|
|
|
Returns the size of the pffi-struct, pffi-enum or pffi-type.
|
|
|
|
#### pffi-align-of <a name="pffi-align-of"></a>
|
|
|
|
**pffi-align-of** type -> number
|
|
|
|
Returns the align of the type.
|
|
|
|
#### pffi-define-library <a name="pffi-define-library"></a>
|
|
|
|
**pffi-define-library** headers shared-object-name [options] -> object
|
|
|
|
Load given shared object automatically searching many predefined paths.
|
|
|
|
Takes as argument a list of C headers, these are for the compiler ones. And an shared-object name,
|
|
used by the dynamic FFI's. The name of the shared object should not contain suffix like .so or
|
|
.dll. Nor should it contain any prefix like "lib".
|
|
|
|
Additional options argument can be provided, theys should be a pair with a
|
|
keyword. The options are:
|
|
|
|
- additional-versions
|
|
- Search for additional versions of shared object, given shared object "c" and additional
|
|
versions "6" "7" on linux the files "libc", "libc.6", "libc.7" are searched for.
|
|
- Can be either numbers or strings
|
|
- additional-paths
|
|
- Give additional paths to search shared objects for
|
|
|
|
Example:
|
|
|
|
(cond-expand
|
|
(windows (pffi-define-library libc-stdlib
|
|
'("stdlib.h")
|
|
"ucrtbase"
|
|
'((additional-versions ("0" "6"))
|
|
(additiona-paths (".")))))
|
|
(else (pffi-define-library libc-stdlib
|
|
(list "stdlib.h")
|
|
"c"
|
|
'((additional-versions ("0" "6"))
|
|
(additiona-paths ("."))))))
|
|
|
|
#### Notes
|
|
|
|
- Do not cond-expand inside the arguments, that might lead to problems on some
|
|
implementations.
|
|
- Do not store options in variables, that might lead to problems on some
|
|
implementations.
|
|
- Do pass the headers using quote
|
|
- As '(... and not (list...
|
|
- Do pass the options using quote
|
|
- As '(... and not (list...
|
|
|
|
#### pffi-pointer-null <a name="pffi-pointer-null"></a>
|
|
|
|
**pffi-pointer-null** -> pointer
|
|
|
|
Returns a new NULL pointer.
|
|
|
|
#### pffi-pointer-null? <a name="pffi-pointer-null?"></a>
|
|
|
|
**pffi-pointer-null?** pointer -> boolean
|
|
|
|
Returns #t if given pointer is null pointer, #f otherwise.
|
|
|
|
#### pffi-pointer-allocate <a name="pffi-pointer-allocate"></a>
|
|
|
|
**pffi-pointer-allocate** size -> pointer
|
|
|
|
Returns newly allocated pointer of given size.
|
|
|
|
#### pffi-pointer-address <a name="pffi-pointer-address"></a>
|
|
|
|
**pffi-pointer-address** pointer -> number
|
|
|
|
Returns the address of given pointer as number.
|
|
|
|
#### pffi-pointer? <a name="pffi-pointer?"></a>
|
|
|
|
**pffi-pointer?** object -> boolean
|
|
|
|
Returns #t if given object is pointer, #f otherwise.
|
|
|
|
#### pffi-pointer-free <a name="pffi-pointer-free"></a>
|
|
|
|
**pffi-pointer-free** pointer
|
|
|
|
Frees given pointer.
|
|
|
|
#### pffi-pointer-set! <a name="pffi-pointer-set!"></a>
|
|
|
|
**pffi-pointer-set!** pointer type offset value
|
|
|
|
Sets the value on a pointer on given offset. For example:
|
|
|
|
(define p (pffi-pointer-allocate 128))
|
|
(pffi-pointer-set! p 'int 64 100)
|
|
|
|
Would set the offset of 64, on pointer p to value 100.
|
|
|
|
#### pffi-pointer-get <a name="pffi-pointer-get"></a>
|
|
|
|
**pffi-pointer-get** pointer type offset -> object
|
|
|
|
Gets the value from a pointer on given offset. For example:
|
|
|
|
(define p (pffi-pointer-allocate 128))
|
|
(pffi-pointer-set! p 'int 64 100)
|
|
(pffi-pointer-get p 'int 64)
|
|
> 100
|
|
|
|
#### pffi-string->pointer <a name="pffi-string-to-pointer"></a>
|
|
|
|
**pffi-string->pointer** string -> pointer
|
|
|
|
Makes pointer out of a given string.
|
|
|
|
#### pffi-pointer->string <a name="pffi-pointer-to-string"></a>
|
|
|
|
**pffi-pointer->string** pointer -> string
|
|
|
|
Makes string out of a given pointer.
|
|
|
|
#### pffi-struct-make <a name="pffi-struct-make"></a>
|
|
|
|
**pffi-struct-make** c-type members . pointer -> pffi-struct
|
|
|
|
Creates a new pffi-struct and allocates pointer for it. The members argument is a list of member
|
|
names and types. For example:
|
|
|
|
(define color (pffi-struct-make 'color '((int8 . r) (int8 . g) (int8 . b) (int8 .a ))))
|
|
(define test (pffi-struct-make "struct test" '((int8 . r) (int8 . g) (int8 . b) (int8 .a ))))
|
|
|
|
C-type argument can be symbol or a string.
|
|
|
|
#### pffi-struct-pointer <a name="pffi-struct-pointer"></a>
|
|
|
|
**pffi-struct-pointer** pffi-struct -> pointer
|
|
|
|
Returns the pointer that holds the struct content. You need to use this when passing a struct as
|
|
a pointer to foreign functions.
|
|
|
|
(define s (pffi-struct-make 'test '((int . r) (int . g) (int . b))))
|
|
(pffi-struct-pointer s)
|
|
|
|
#### pffi-struct-offset-get <a name="pffi-struct-offset-get"></a>
|
|
|
|
**pffi-struct-offset-get** member-name -> number
|
|
|
|
Returns the offset of a struct member with given name.
|
|
|
|
#### pffi-struct-get <a name="pffi-struct-get"></a>
|
|
|
|
**pffi-struct-get** pffi-struct member-name -> object
|
|
|
|
Returns the value of the givens struct member.
|
|
|
|
#### pffi-struct-set! <a name="pffi-struct-set!"></a>
|
|
|
|
**pffi-struct-set!** pffi-struct member-name value
|
|
|
|
Sets the value of the givens struct member. It is up to you to make sure that the type of value is
|
|
correct.
|
|
|
|
**pffi-list->array** type list
|
|
|
|
Converts given list into C array of given type.
|
|
|
|
**pffi-array->list** type list length
|
|
|
|
Converts given C array into list of given type and length.
|
|
|
|
#### pffi-define <a name="pffi-define"></a>
|
|
|
|
**pffi-define** scheme-name shared-object c-name return-type argument-types
|
|
|
|
Defines a new foreign function to be used from Scheme code. For example:
|
|
|
|
(cond-expand
|
|
(windows (pffi-define-library libc-stdlib '("stdlib.h") "ucrtbase" '("")))
|
|
(else (pffi-define-library libc-stdlib '("stdlib.h") "c" '("" "6"))))
|
|
(pffi-define c-puts libc-stdlib 'puts 'int '(pointer))
|
|
(c-puts "Message brought to you by FFI!")
|
|
|
|
#### pffi-define-callback <a name="pffi-define-callback"></a>
|
|
|
|
**pffi-define-callback** scheme-name return-type argument-types procedure
|
|
|
|
Defines a new Sceme function to be used as callback to C code. For example:
|
|
|
|
; Load the shared library
|
|
(cond-expand
|
|
(windows (pffi-define-library libc-stdlib '("stdlib.h") "ucrtbase" '()))
|
|
(else (pffi-define-library '("stdlib.h") "c" '("" "6"))))
|
|
|
|
; Define C function that takes a callback
|
|
(pffi-define qsort libc-stdlib 'qsort 'void '(pointer int int callback))
|
|
|
|
; Define our callback
|
|
(pffi-define-callback compare
|
|
'int
|
|
'(pointer pointer)
|
|
(lambda (pointer-a pointer-b)
|
|
(let ((a (pffi-pointer-get pointer-a 'int 0))
|
|
(b (pffi-pointer-get pointer-b 'int 0)))
|
|
(cond ((> a b) 1)
|
|
((= a b) 0)
|
|
((< a b) -1)))))
|
|
|
|
; Create new array of ints to be sorted
|
|
(define array (pffi-pointer-allocate (* (pffi-size-of 'int) 3)))
|
|
(pffi-pointer-set! array 'int (* (pffi-size-of 'int) 0) 3)
|
|
(pffi-pointer-set! array 'int (* (pffi-size-of 'int) 1) 2)
|
|
(pffi-pointer-set! array 'int (* (pffi-size-of 'int) 2) 1)
|
|
|
|
(display array)
|
|
(newline)
|
|
;> (3 2 1)
|
|
|
|
; Sort the array
|
|
(qsort array 3 (pffi-size-of 'int) compare)
|
|
|
|
(display array)
|
|
(newline)
|
|
;> (1 2 3)
|
|
|
|
</main>
|