Go to file
retropikzel 7d77b74b7c Fixing the documentation internal links to work 2025-04-07 07:34:59 +03:00
dockerfiles Fixing testing 2025-02-28 14:01:14 +02:00
documentation Update package.scm and documentation 2025-03-13 07:35:05 +02:00
include Progress 2025-03-22 14:41:28 +02:00
old-tests Move to running tests with scheme_runner 2024-09-03 15:15:15 +03:00
retropikzel Add documentation about array utilities 2025-04-07 07:18:13 +03:00
snow/arvyy Cleaning up 2025-03-02 14:28:08 +02:00
src Added C array utils and tests 2025-04-06 17:11:31 +03:00
templates Updated documentation 2025-03-12 16:40:35 +02:00
tests Added C array utils and tests 2025-04-06 17:11:31 +03:00
.gitignore Making room for more tests 2025-03-15 07:06:54 +02:00
Jenkinsfile Fixing testing 2025-02-28 14:08:26 +02:00
LICENSE Fix tests, add pffi-os-name 2024-07-31 21:14:38 +03:00
Makefile Move gauche library building under retropikzel/pffi. Update documentation. 2025-03-28 09:16:16 +02:00
README.md Fixing the documentation internal links to work 2025-04-07 07:34:59 +03:00
build.scm Fixing testing 2025-02-28 14:05:42 +02:00
manifest.scm Type, size and readme fixes 2025-02-19 21:03:20 +02:00
package.scm Update package.scm and documentation 2025-03-13 07:35:05 +02:00
test.rkt Cleaning up 2025-03-02 14:28:08 +02:00

README.md

title version
Portable Foreign Function Interface for R7RS Documentation 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

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.

Current caveats

  • 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

Feature mplementation table

Primitives

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

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!
  • pffi-array-allocate
  • pffi-array?
  • pffi-pointer->array
  • pffi-array-get
  • pffi-array-set!
  • pffi-list->array
  • pffi-array->list

Not started

  • 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
  • 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

Other

  • s7
    • Propably does not need FFI as it is embeddable only
  • Loko
    • Desires no C interop, I can respect that

Documentation

Installation

Download the latest release from 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

Some implementations need extra step of compiling the library. Change directory to ./snow/retropikzel/pffi and run command corresponding to your implementation.

Chibi

make -C ./snow/retropikzel/pffi chibi-pffi.so
Gauche

make -C ./snow/retropikzel/pffi gauche-pffi.so

Dependencies

Some implementations have extra dependencies/requirements beyond just the library.

Chibi

Building depends on libffi.

Debian/Ubuntu/Mint install with:

apt install libffi-dev

Chicken

Needs r7rs egg, install with:

chicken-install r7rs

Gauche

Building depends on libffi.

Debian/Ubuntu/Mint install with:

apt install libffi-dev

Racket

Needs racket-r7rs, install with:

raco pkg install --auto r7rs

Kawa

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

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

pffi-init

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

pffi-size-of

pffi-size-of object -> number

Returns the size of the pffi-struct, pffi-enum or pffi-type.

pffi-align-of

pffi-align-of type -> number

Returns the align of the type.

pffi-define-library

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

pffi-pointer-null -> pointer

Returns a new NULL pointer.

pffi-pointer-null?

pffi-pointer-null? pointer -> boolean

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

pffi-pointer-allocate

pffi-pointer-allocate size -> pointer

Returns newly allocated pointer of given size.

pffi-pointer-address

pffi-pointer-address pointer -> number

Returns the address of given pointer as number.

pffi-pointer?

pffi-pointer? object -> boolean

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

pffi-pointer-free

pffi-pointer-free pointer

Frees given pointer.

pffi-pointer-set!

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

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

pffi-string->pointer string -> pointer

Makes pointer out of a given string.

pffi-pointer->string

pffi-pointer->string pointer -> string

Makes string out of a given pointer.

pffi-struct-make

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

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

pffi-struct-offset-get member-name -> number

Returns the offset of a struct member with given name.

pffi-struct-get

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

Returns the value of the givens struct member.

pffi-struct-set!

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-array-allocate

pffi-array-allocate type size

Allocates pointer array of given type and size.

pffi-array?

pffi-array? object

Returns #t of given object is array, #f otherwise.

pffi-pointer->array

pffi-pointer->array pointer type size

Converts given pointer to an array of giben type and size.

pffi-array-get

pffi-array-get array index

Returns the value of given index from given array.

pffi-array-set!

pffi-array-set! array index value

Sets the given value of given index in given array.

pffi-list->array

pffi-list->array type list

Converts given list into C array of given type.

pffi-array->list

pffi-array->list type list length

Converts given C array into list of given type and length.

pffi-define

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

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)