Go to file
retropikzel e84865b18b Small improvements here and there 2025-01-31 19:26:17 +02:00
docs Updat documentation 2025-01-02 19:10:18 +02:00
old-tests Move to running tests with scheme_runner 2024-09-03 15:15:15 +03:00
retropikzel Small improvements here and there 2025-01-31 19:26:17 +02:00
site Fixing file structure 2024-09-22 18:03:48 +03:00
.gitignore Make the struct members accessors and add tests 2025-01-18 10:23:53 +02:00
Dockerfile.jenkins Making the build work 2024-09-26 19:33:39 +03:00
HACKING.md Updated the support table. Added cond-expand to export side in .sld file. 2024-11-14 19:50:18 +02:00
IMPLEMENTATION_NOTES.md Added pffi-define support for Chibi 2024-11-09 10:28:56 +00:00
Jenkinsfile Lots of fixes 2024-10-04 15:48:19 +03:00
LICENSE Fix tests, add pffi-os-name 2024-07-31 21:14:38 +03:00
Makefile Small improvements here and there 2025-01-31 19:26:17 +02:00
README.md Fix auto-load versions and paths handling 2025-01-31 18:23:57 +02:00
composition.scm Move to running tests with scheme_runner 2024-09-03 15:15:15 +03:00
gerbil.pkg Added most of the support for mosh, some bugs remain and couple of unimplemented procedures. Got Gerbil to load files. 2024-11-07 16:34:22 +00:00
jenkins_entrypoint.sh Making the build work 2024-09-26 19:29:27 +03:00
kawa.jar Added most of Kawa support 2024-05-06 20:45:31 +03:00
libtest.c Small improvements here and there 2025-01-31 19:26:17 +02:00
libtest.h Small improvements here and there 2025-01-31 19:26:17 +02:00
manifest.scm Fixing the load interface 2025-01-31 12:15:36 +02:00
mkdocs.yml Update readme, improve documentation 2024-08-25 16:39:05 +01:00
package.scm Add package.scm 2024-09-20 10:39:14 +03:00
test.scm Small improvements here and there 2025-01-31 19:26:17 +02:00

README.md

Portable Foreign Function Interface for R7RS schemes

Foreign function interface that is supported on multiple R7RS Sceheme implementations.

Any help in form of constructive advice and bug reports are appreciated.

Issue trackers

Maling lists

Jenkins.

Table of contents

Goals

  • 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

  • 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

Currently the interface of the library is in okay shape. It propably will not change much but no guarantees are being made just yet.

Due to supporting many different Scheme implementations, different parts of this software are in different stage. As a whole it is still in alpha stage. That said the interface should not be changing anymore and support for some implementations are in beta.

Implementation status

Alpha

Anything not in beta or done, not recommended to use.

Beta

Usage can be started but might still be quite buggy.

  • Guile
  • Sagittarius
  • Racket

Done

Usage recommended.

  • None yet

Implementation table

Chibi Chicken 5 Cyclone Gambit Gauche Gerbil Guile Kawa Larceny Mosh Racket Sagittarius Skint STklos tr7 Ypsilon
pffi-init X X X X X X X X X X X X X X
pffi-size-of X X X X X X X X X X
pffi-shared-object-auto-load X X X X X X X X X
pffi-shared-object-load X X X X X X X X X
pffi-pointer-null X X X X X X X X X
pffi-pointer-null? X X X X X X X X X
pffi-pointer-allocate X X X X X X X X X
pffi-pointer? X X X X X X X X X
pffi-pointer-free X X X X X X X X X
pffi-pointer-set! X X X X X X X X
pffi-pointer-get X X X X X X X X
pffi-string->pointer X X X X X X X X
pffi-pointer->string X X X X X X X X
pffi-struct-make X X X X X X X X X X X X X X X X
pffi-struct-size X X X X X X X X X X X X X X X X
pffi-struct-pointer X X X X X X X X X X X X X X X X
pffi-struct-offset-get X X X X X X X X X X X X X X X X
pffi-struct-get X X X X X X X X X X X X X X X X
pffi-struct-set! X X X X X X X X X X X X X X X X
pffi-struct-dereference X X X X X
pffi-define X X X X X X X X
pffi-define-callback X X X X X

Other implementations

  • LIPS
    • Will work on nodejs by using some C FFI library from npm
    • Javascript side needs design
  • Biwascheme
    • Will work on nodejs by using some C FFI library from npm
    • Javascript side needs design
  • MIT-Scheme
    • Need to study the implementation more
  • s7
    • Propably does not need FFI?
  • Airship
    • Need to study the implementation more
  • Other gambit targets
    • 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
    • Need to study the implementation more
  • prescheme
    • Need to study the implementation more
  • Loko
    • Desires no C interop, I can respect that

Documentation

Usage notes

  • Chibi
    • Install libffi-dev
    • Build with:
      • chibi-ffi retropikzel/r7rs-pffi/r7rs-pffi-chibi.stub
      • ${CC} -o retropikzel/r7rs-pffi/r7rs-pffi-chibi.so -fPIC -shared retropikzel/r7rs-pffi/r7rs-pffi-chibi.c -lchibi-scheme -lffi
  • Chicken 5
  • Kawa
    • Needs at least Java version 22
    • Needs 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
  • Racket

Reference

Types

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

Some of these are procedures and some macros, it might also change implementation to implementation.

pffi-init

Always call this first, on most implementation it does nothing but some implementations might need initialisation run.

pffi-size-of type -> number

Returns the size of the type.

pffi-align-of type -> number

Returns the align of the type.

pffi-shared-object-auto-load 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, which should be a list of lists starting 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.
  • additional-paths
    • Give additional paths to search shared objects for

Example:

(define libc-stdlib
  (cond-expand
    (windows (pffi-shared-object-auto-load (list "stdlib.h") "ucrtbase"))
    (else (pffi-shared-object-auto-load (list "stdlib.h") "c" '((additional-versions (".6")))))))
pffi-shared-object-load headers path [options]

It is recommended to use the pffi-shared-object-auto-load instead of this directly.

Headers is a list of strings needed to be included, for example

(list "curl/curl.h")

Path is the full path of the shared object without any "lib" prefix or ".so/.dll" suffix. For example:

"curl"

Options:

  • versions
    • List of different versions of library to try, for example (list ".0" ".1")
pffi-pointer-null -> pointer

Returns a new NULL pointer.

pffi-pointer-null? pointer -> boolean

Returns #t if given pointer is null pointer, #f otherwise.

pffi-pointer-allocate size -> pointer

Returns newly allocated pointer of given size.

pffi-pointer? object -> boolean

Returns #t if given object is pointer, #f otherwise.

pffi-pointer-free pointer

Frees given pointer.

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 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 string -> pointer

Makes pointer out of a given string.

pffi-pointer->string pointer -> string

Makes string out of a given pointer.

pffi-struct-make name 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 s (pffi-struct-make 'test '((int . r) (int . g) (int . b))))
pffi-struct-size pffi-struct -> number

Returns the size of a given pffi-struct. For example:

(define s (pffi-struct-make 'test '((int . r) (int . g) (int . b))))
(pffi-struct-size s)
> 12
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 member-name -> number

Returns the offset of a struct member with given name.

pffi-struct-get pffi-struct member-name -> object

Returns the value of the givens struct member.

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-define scheme-name shared-object c-name return-type argument-types

Defines a new foreign function to be used from Scheme code. For example:

(define libc-stdlib
    (cond-expand
        (windows (pffi-shared-object-auto-load (list "stdlib.h") (list) "ucrtbase" (list "")))
        (else (pffi-shared-object-auto-load (list "stdlib.h") (list) "c" (list "" ".6")))))
(pffi-define c-puts libc-stdlib 'puts 'int (list 'pointer))
(c-puts "Message brought to you by FFI!")
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
(define libc-stdlib
    (cond-expand
        (windows (pffi-shared-object-auto-load (list "stdlib.h") (list) "ucrtbase" (list "")))
        (else (pffi-shared-object-auto-load (list "stdlib.h") (list) "c" (list "" ".6")))))

; Define C function that takes a callback
(pffi-define qsort libc-stdlib 'qsort 'void (list 'pointer 'int 'int 'callback))

; Define our callback
(pffi-define-callback compare
                      'int
                      (list '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)