Git commit renaming and restructuring the library

This commit is contained in:
retropikzel 2025-04-26 12:21:02 +03:00
parent 04d4e43b04
commit 8d012a5ba2
34 changed files with 743 additions and 735 deletions

1
.gitignore vendored
View File

@ -45,3 +45,4 @@ tests/retropikzel
testfile.test
tests/testfile.test
snow
foreign/c/lib

View File

@ -9,64 +9,64 @@ docs:
mkdir -p documentation
pandoc --standalone \
--template templates/documentation.html README.md \
> documentation/R7RS-PFFI.html
> documentation/foreign-c.html
pandoc -t html5 \
--pdf-engine=weasyprint \
--css templates/css/pdf-documentation.css \
-o documentation/R7RS-PFFI.pdf \
-o documentation/foreign-c.pdf \
README.md
chibi:
make -C retropikzel/pffi chibi
make -C foreign/c chibi
chicken:
make -C retropikzel/pffi chicken
make -C foreign/c chicken
cyclone:
make -C retropikzel/pffi cyclone
make -C foreign/c cyclone
gambit:
make -C retropikzel/pffi gambit
make -C foreign/c gambit
gauche:
make -C retropikzel/pffi gauche
make -C foreign/c gauche
gerbil:
make -C retropikzel/pffi gerbil
make -C foreign/c gerbil
guile:
make -C retropikzel/pffi guile
make -C foreign/c guile
kawa:
make -C retropikzel/pffi kawa
make -C foreign/c kawa
larceny:
make -C retropikzel/pffi larceny
make -C foreign/c larceny
mosh:
make -C retropikzel/pffi mosh
make -C foreign/c mosh
racket:
make -C retropikzel/pffi racket
make -C foreign/c racket
sagittarius:
make -C retropikzel/pffi sagittarius
make -C foreign/c sagittarius
skint:
make -C retropikzel/pffi skint
make -C foreign/c skint
stklos:
make -C retropikzel/pffi stklos
make -C foreign/c stklos
tr7:
make -C retropikzel/pffi tr7
make -C foreign/c tr7
ypsilon:
make -C retropikzel/pffi tr7
make -C foreign/c tr7
test-compile-r7rs: tmp/test/libtest.o tmp/test/libtest.so tmp/test/libtest.a
make ${COMPILE_R7RS}
cp -r retropikzel tmp/test/
cp -r foreign tmp/test/
cp tests/*.scm tmp/test/
cp tests/c-include/libtest.h tmp/test/
cd tmp/test && \
@ -93,29 +93,13 @@ tmp/test/libtest.a: tmp/test/libtest.o tests/c-src/libtest.c
ar rcs tmp/test/libtest.a tmp/test/libtest.o
clean:
@rm -rf retropikzel/pffi/pffi.c
@rm -rf retropikzel/pffi/*.o*
@rm -rf retropikzel/pffi/*.so
@rm -rf retropikzel/pffi/*.meta
@rm -rf retropikzel/pffi/retropikzel.*
@rm -rf retropikzel/pffi/compiled
@rm -rf retropikzel.*
find . -name "*.meta" -delete
@rm -rf test/pffi-define
@rm -rf test/*gambit*
find . -name "*.link" -delete
find . -name "*.o" -delete
find . -name "*.o[1-9]" -delete
find . -name "*.so" -delete
find . -name "*.a" -delete
find . -name "*.class" -delete
@rm -rf test
find . -name "core.1" -delete
find . -name "*@gambit*" -delete
rm -rf retropikzel/pffi.c
rm -rf tests/compliance.c*
rm -rf tests/compliance.o
rm -rf tests/compliance.so
rm -rf tests/compliance
rm -rf tests/retropikzel.*.import.scm
rm -rf tmp

654
ODL_README.md Normal file
View File

@ -0,0 +1,654 @@
---
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)
- [Dependencies](#dependencies)
- [Chibi](#dependencies-chibi)
- [Chicken](#dependencies-chicken)
- [Gauche](#dependencies-gauche)
- [Racket](#dependencies-racket)
- [Kawa](#dependencies-kawa)
- [Installation](#installation)
- [Project local](#installation-project-local)
- [Linux](#installation-project-local-linux)
- [Windows](#installation-project-local-windows)
- [System global](#installation-system-global)
- [Reference](#reference)
- [Types](#types)
- [Environment variables](#environment-variables)
- [PFFI\_LOAD\_PATH](#environment-variables-pffi-load-path)
- [Procedures and macros](#procedures-and-macros)
- [pffi-init](#pffi-init)
- [c-size-of](#c-size-of)
- [pffi-align-of](#pffi-align-of)
- [define-c-library](#define-c-library)
- [make-c-null](#make-c-null)
- [c-null?](#is-c-null)
- [make-c-bytevector ](#make-c-bytevector )
- [pffi-pointer-address](#pffi-pointer-address)
- [c-bytevector?](#is-c-bytevector)
- [c-free](#c-free)
- [pffi-pointer-set!](#pffi-pointer-set!)
- [pffi-pointer-get](#pffi-pointer-get)
- [utf8->c-bytevector](#utf8-into-c-bytevector)
- [c-bytevector->sring](#c-bytevector-into-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-array-allocate](#pffi-array-allocate)
- [pffi-array-pointer](#pffi-array-pointer)
- [pffi-array?](#pffi-array)
- [pffi-pointer->array](#pffi-pointer->array)
- [pffi-array-get](#pffi-array-get)
- [pffi-array-set!](#pffi-array-set!)
- [pffi-list->array](#pffi-list->array)
- [pffi-array->list](#pffi-array->list)
- [define-c-procedure](#define-c-procedure)
- [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>
- 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>
In alpha.
### 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 number 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>
| | c-size-of | define-c-library | c-bytevector? | pffi-pointer-set! | pffi-pointer-get | define-c-procedure | pffi-define-callback |
|--------------|:------------:|:-------------------:|:-------------:|:-----------------:|:----------------:|:-------------------:|:--------------------:|
| Chibi | X | X | X | X | X | X | |
| Chicken | X | X | X | X | X | X | X |
| Cyclone | X | X | X | X | X | X | |
| Gambit | X | | | | | | |
| Gauche | X | X | X | X | X | X | |
| Gerbil | | | | | | | |
| Guile | X | X | X | X | X | X | X |
| Kawa | X | X | X | X | X | X | X |
| Larceny | | | | | | | |
| Mosh | X | X | X | X | X | X | X |
| Racket | X | X | X | X | X | X | X |
| Saggittarius | X | X | X | X | X | X | X |
| Skint | | | | | | | |
| Stklos | X | X | X | | | | |
| tr7 | | | | | | | |
| Ypsilon | 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.
- make-c-bytevector
- make-c-null
- c-null?
- pffi-pointer-address
- c-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
<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>
### Dependencies
<a name="dependencies"></a>
Some implementations have extra dependencies/requirements beyond just the
library.
#### Chibi
<a name="dependencies-chibi"></a>
Building depends on libffi.
Debian/Ubuntu/Mint install with:
apt install libffi-dev
#### Chicken
<a name="dependencies-chicken"></a>
Chicken needs r7rs egg installed. Install it with:
chicken-install r7rs
#### Gauche
<a name="dependencies-gauche"></a>
Building depends on libffi.
Debian/Ubuntu/Mint install with:
apt install libffi-dev
#### 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 these flags before any other arguments:
- \-J--add-exports=java.base/jdk.internal.foreign.abi=ALL-UNNAMED
- \-J--add-exports=java.base/jdk.internal.foreign.layout=ALL-UNNAMED
- \-J--add-exports=java.base/jdk.internal.foreign=ALL-UNNAMED
- \-J--enable-native-access=ALL-UNNAMED
If you are running kawa.jar with plain java then give same arguments to java
without the -J prefix.
### Installation
<a name="installation"></a>
Since the project is under active development is best to clone it from git,
### Project local
<a name="installation-project-local"></a>
#### Linux
<a name="installation-project-local-linux"></a>
Assuming you have a project and your libraries live in directory called snow
in it:
git clone https://git.sr.ht/~retropikzel/r7rs-pffi
mkdir -p snow
cp -r r7rs-pffi/retropikzel snow/
cd snow/retropikzel/pffi
make <SCHEME>
#### Windows
<a name="installation-project-local-windows"></a>
There is no build scripts yet for Windows, that said many implementations work
without compiling anything. If you run this and it says "There is notching to
build for SCHEME" then you should be good to go.
### System global
<a name="installation-system-global"></a>
Still work in progress.
## 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
### Types
<a name="types"></a>
### Environment variables
<a name="environment-variables"></a>
Setting environment variables like this on Windows works for this library:
set "PFFI_LOAD_PATH=C:\Program Files (x86)/foo/bar"
#### PFFI\_LOAD\_PATH
<a name="environment-variables-pffi-load-path"></a>
To add more paths to where pffi looks for libraries set PFFI\_LOAD\_PATH to
paths separated by ; on windows, and : on other operating systems.
### 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.
#### c-size-of
<a name="c-size-of"></a>
**c-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.
#### define-c-library
<a name="define-c-library"></a>
**define-c-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 (define-c-library libc-stdlib
'("stdlib.h")
"ucrtbase"
'((additional-versions ("0" "6"))
(additiona-paths (".")))))
(else (define-c-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...
#### make-c-null
<a name="make-c-null"></a>
**make-c-null** -> pointer
Returns a new NULL pointer.
#### c-null?
<a name="is-c-null"></a>
**c-null?** pointer -> boolean
Returns #t if given pointer is null pointer, #f otherwise.
#### make-c-bytevector
<a name="make-c-bytevector "></a>
(make-c-bytevector *k*)
(make-c-bytevector *k* *fill*)
Returns a newly allocated C bytevector(pointer) of length k. If byte is given,
then all elements of the C bytevector are initialized to byte, otherwise the
contents of each element are unspecified.
#### pffi-pointer-address
<a name="pffi-pointer-address"></a>
**pffi-pointer-address** pointer -> pointer
Returns the address of given pointer inside a pointer. This is used when
passing pointers to pointers to foreign procedures. This is similar to the
c's &. One **important difference** is that after you have passed a pointer to
the procedure you must get value from it back to the pointer which address you
are passing. Example:
(define input-pointer (make-c-bytevector <needed size>))
(define input-pointer-address (pffi-pointer-address input-pointer))
(<foreign-procedure-that takes &pointer as argument> input-pointer-address)
(set! input-pointer (pffi-pointer-get input-pointer-address 'pointer 0))
#### c-bytevector?
<a name="pffi-pointer"></a>
**c-bytevector?** object -> boolean
Returns #t if given object is pointer, #f otherwise.
#### c-free
<a name="c-free"></a>
**c-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 (make-c-bytevector 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 (make-c-bytevector 128))
(pffi-pointer-set! p 'int 64 100)
(pffi-pointer-get p 'int 64)
> 100
#### utf8->c-bytevector
<a name="utf8-into-c-bytevector"></a>
**utf8->c-bytevector** string -> pointer
Makes pointer out of a given string.
#### c-bytevector->string
<a name="c-bytevector-into-string"></a>
**c-bytevector->sring** 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-array-allocate
<a name="pffi-array-allocate"></a>
**pffi-array-allocate** type size
Allocates pointer array of given type and size.
#### pffi-array-pointer
<a name="pffi-array-pointer"></a>
**pffi-array-pointer** array
Returns the pointer of the array.
#### pffi-array?
<a name="pffi-array"></a>
**pffi-array?** object
Returns #t of given object is array, #f otherwise.
#### pffi-pointer->array
<a name="pffi-pointer->array"></a>
**pffi-pointer->array** pointer type size
Converts given pointer to an array of giben type and size.
#### pffi-array-get
<a name="pffi-array-get"></a>
**pffi-array-get** array index
Returns the value of given index from given array.
#### pffi-array-set!
<a name="pffi-array-set!"></a>
**pffi-array-set!** array index value
Sets the given value of given index in given array.
#### pffi-list->array
<a name="pffi-list->array"></a>
**pffi-list->array** type list
Converts given list into C array of given type.
#### pffi-array->list
<a name="pffi-array->list"></a>
**pffi-array->list** type list length
Converts given C array into list of given type and length.
#### define-c-procedure
<a name="define-c-procedure"></a>
**define-c-procedure** 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 (define-c-library libc-stdlib '("stdlib.h") "ucrtbase" '("")))
(else (define-c-library libc-stdlib '("stdlib.h") "c" '("" "6"))))
(define-c-procedure 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 (define-c-library libc-stdlib '("stdlib.h") "ucrtbase" '()))
(else (define-c-library '("stdlib.h") "c" '("" "6"))))
; Define C function that takes a callback
(define-c-procedure 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 (make-c-bytevector (* (c-size-of 'int) 3)))
(pffi-pointer-set! array 'int (* (c-size-of 'int) 0) 3)
(pffi-pointer-set! array 'int (* (c-size-of 'int) 1) 2)
(pffi-pointer-set! array 'int (* (c-size-of 'int) 2) 1)
(display array)
(newline)
;> (3 2 1)
; Sort the array
(qsort array 3 (c-size-of 'int) compare)
(display array)
(newline)
;> (1 2 3)
</main>

653
README.md
View File

@ -1,654 +1,13 @@
---
title: Portable Foreign Function Interface for R7RS Documentation
version: 0.6.0
version: 0.10.0
---
# Portable Foreign Function Interface for R7RS
# (foreign c)
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.
(foreign c) is a C foreign function interface (FFI) library 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)
The new readme is a work in progress.
[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)
- [Dependencies](#dependencies)
- [Chibi](#dependencies-chibi)
- [Chicken](#dependencies-chicken)
- [Gauche](#dependencies-gauche)
- [Racket](#dependencies-racket)
- [Kawa](#dependencies-kawa)
- [Installation](#installation)
- [Project local](#installation-project-local)
- [Linux](#installation-project-local-linux)
- [Windows](#installation-project-local-windows)
- [System global](#installation-system-global)
- [Reference](#reference)
- [Types](#types)
- [Environment variables](#environment-variables)
- [PFFI\_LOAD\_PATH](#environment-variables-pffi-load-path)
- [Procedures and macros](#procedures-and-macros)
- [pffi-init](#pffi-init)
- [c-size-of](#c-size-of)
- [pffi-align-of](#pffi-align-of)
- [define-c-library](#define-c-library)
- [make-c-null](#make-c-null)
- [c-null?](#is-c-null)
- [make-c-bytevector ](#make-c-bytevector )
- [pffi-pointer-address](#pffi-pointer-address)
- [c-bytevector?](#is-c-bytevector)
- [c-free](#c-free)
- [pffi-pointer-set!](#pffi-pointer-set!)
- [pffi-pointer-get](#pffi-pointer-get)
- [utf8->c-bytevector](#utf8-into-c-bytevector)
- [c-bytevector->sring](#c-bytevector-into-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-array-allocate](#pffi-array-allocate)
- [pffi-array-pointer](#pffi-array-pointer)
- [pffi-array?](#pffi-array)
- [pffi-pointer->array](#pffi-pointer->array)
- [pffi-array-get](#pffi-array-get)
- [pffi-array-set!](#pffi-array-set!)
- [pffi-list->array](#pffi-list->array)
- [pffi-array->list](#pffi-array->list)
- [define-c-procedure](#define-c-procedure)
- [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>
- 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>
In alpha.
### 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 number 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>
| | c-size-of | define-c-library | c-bytevector? | pffi-pointer-set! | pffi-pointer-get | define-c-procedure | pffi-define-callback |
|--------------|:------------:|:-------------------:|:-------------:|:-----------------:|:----------------:|:-------------------:|:--------------------:|
| Chibi | X | X | X | X | X | X | |
| Chicken | X | X | X | X | X | X | X |
| Cyclone | X | X | X | X | X | X | |
| Gambit | X | | | | | | |
| Gauche | X | X | X | X | X | X | |
| Gerbil | | | | | | | |
| Guile | X | X | X | X | X | X | X |
| Kawa | X | X | X | X | X | X | X |
| Larceny | | | | | | | |
| Mosh | X | X | X | X | X | X | X |
| Racket | X | X | X | X | X | X | X |
| Saggittarius | X | X | X | X | X | X | X |
| Skint | | | | | | | |
| Stklos | X | X | X | | | | |
| tr7 | | | | | | | |
| Ypsilon | 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.
- make-c-bytevector
- make-c-null
- c-null?
- pffi-pointer-address
- c-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
<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>
### Dependencies
<a name="dependencies"></a>
Some implementations have extra dependencies/requirements beyond just the
library.
#### Chibi
<a name="dependencies-chibi"></a>
Building depends on libffi.
Debian/Ubuntu/Mint install with:
apt install libffi-dev
#### Chicken
<a name="dependencies-chicken"></a>
Chicken needs r7rs egg installed. Install it with:
chicken-install r7rs
#### Gauche
<a name="dependencies-gauche"></a>
Building depends on libffi.
Debian/Ubuntu/Mint install with:
apt install libffi-dev
#### 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 these flags before any other arguments:
- \-J--add-exports=java.base/jdk.internal.foreign.abi=ALL-UNNAMED
- \-J--add-exports=java.base/jdk.internal.foreign.layout=ALL-UNNAMED
- \-J--add-exports=java.base/jdk.internal.foreign=ALL-UNNAMED
- \-J--enable-native-access=ALL-UNNAMED
If you are running kawa.jar with plain java then give same arguments to java
without the -J prefix.
### Installation
<a name="installation"></a>
Since the project is under active development is best to clone it from git,
### Project local
<a name="installation-project-local"></a>
#### Linux
<a name="installation-project-local-linux"></a>
Assuming you have a project and your libraries live in directory called snow
in it:
git clone https://git.sr.ht/~retropikzel/r7rs-pffi
mkdir -p snow
cp -r r7rs-pffi/retropikzel snow/
cd snow/retropikzel/pffi
make <SCHEME>
#### Windows
<a name="installation-project-local-windows"></a>
There is no build scripts yet for Windows, that said many implementations work
without compiling anything. If you run this and it says "There is notching to
build for SCHEME" then you should be good to go.
### System global
<a name="installation-system-global"></a>
Still work in progress.
## 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
### Types
<a name="types"></a>
### Environment variables
<a name="environment-variables"></a>
Setting environment variables like this on Windows works for this library:
set "PFFI_LOAD_PATH=C:\Program Files (x86)/foo/bar"
#### PFFI\_LOAD\_PATH
<a name="environment-variables-pffi-load-path"></a>
To add more paths to where pffi looks for libraries set PFFI\_LOAD\_PATH to
paths separated by ; on windows, and : on other operating systems.
### 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.
#### c-size-of
<a name="c-size-of"></a>
**c-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.
#### define-c-library
<a name="define-c-library"></a>
**define-c-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 (define-c-library libc-stdlib
'("stdlib.h")
"ucrtbase"
'((additional-versions ("0" "6"))
(additiona-paths (".")))))
(else (define-c-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...
#### make-c-null
<a name="make-c-null"></a>
**make-c-null** -> pointer
Returns a new NULL pointer.
#### c-null?
<a name="is-c-null"></a>
**c-null?** pointer -> boolean
Returns #t if given pointer is null pointer, #f otherwise.
#### make-c-bytevector
<a name="make-c-bytevector "></a>
(make-c-bytevector *k*)
(make-c-bytevector *k* *fill*)
Returns a newly allocated C bytevector(pointer) of length k. If byte is given,
then all elements of the C bytevector are initialized to byte, otherwise the
contents of each element are unspecified.
#### pffi-pointer-address
<a name="pffi-pointer-address"></a>
**pffi-pointer-address** pointer -> pointer
Returns the address of given pointer inside a pointer. This is used when
passing pointers to pointers to foreign procedures. This is similar to the
c's &. One **important difference** is that after you have passed a pointer to
the procedure you must get value from it back to the pointer which address you
are passing. Example:
(define input-pointer (make-c-bytevector <needed size>))
(define input-pointer-address (pffi-pointer-address input-pointer))
(<foreign-procedure-that takes &pointer as argument> input-pointer-address)
(set! input-pointer (pffi-pointer-get input-pointer-address 'pointer 0))
#### c-bytevector?
<a name="pffi-pointer"></a>
**c-bytevector?** object -> boolean
Returns #t if given object is pointer, #f otherwise.
#### c-free
<a name="c-free"></a>
**c-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 (make-c-bytevector 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 (make-c-bytevector 128))
(pffi-pointer-set! p 'int 64 100)
(pffi-pointer-get p 'int 64)
> 100
#### utf8->c-bytevector
<a name="utf8-into-c-bytevector"></a>
**utf8->c-bytevector** string -> pointer
Makes pointer out of a given string.
#### c-bytevector->string
<a name="c-bytevector-into-string"></a>
**c-bytevector->sring** 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-array-allocate
<a name="pffi-array-allocate"></a>
**pffi-array-allocate** type size
Allocates pointer array of given type and size.
#### pffi-array-pointer
<a name="pffi-array-pointer"></a>
**pffi-array-pointer** array
Returns the pointer of the array.
#### pffi-array?
<a name="pffi-array"></a>
**pffi-array?** object
Returns #t of given object is array, #f otherwise.
#### pffi-pointer->array
<a name="pffi-pointer->array"></a>
**pffi-pointer->array** pointer type size
Converts given pointer to an array of giben type and size.
#### pffi-array-get
<a name="pffi-array-get"></a>
**pffi-array-get** array index
Returns the value of given index from given array.
#### pffi-array-set!
<a name="pffi-array-set!"></a>
**pffi-array-set!** array index value
Sets the given value of given index in given array.
#### pffi-list->array
<a name="pffi-list->array"></a>
**pffi-list->array** type list
Converts given list into C array of given type.
#### pffi-array->list
<a name="pffi-array->list"></a>
**pffi-array->list** type list length
Converts given C array into list of given type and length.
#### define-c-procedure
<a name="define-c-procedure"></a>
**define-c-procedure** 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 (define-c-library libc-stdlib '("stdlib.h") "ucrtbase" '("")))
(else (define-c-library libc-stdlib '("stdlib.h") "c" '("" "6"))))
(define-c-procedure 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 (define-c-library libc-stdlib '("stdlib.h") "ucrtbase" '()))
(else (define-c-library '("stdlib.h") "c" '("" "6"))))
; Define C function that takes a callback
(define-c-procedure 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 (make-c-bytevector (* (c-size-of 'int) 3)))
(pffi-pointer-set! array 'int (* (c-size-of 'int) 0) 3)
(pffi-pointer-set! array 'int (* (c-size-of 'int) 1) 2)
(pffi-pointer-set! array 'int (* (c-size-of 'int) 2) 1)
(display array)
(newline)
;> (3 2 1)
; Sort the array
(qsort array 3 (c-size-of 'int) compare)
(display array)
(newline)
;> (1 2 3)
</main>

View File

@ -1,5 +1,5 @@
(define-library
(retropikzel pffi) ; (foreign r7rs)? (foreign c)?
(foreign c)
(cond-expand
(chibi
(import (scheme base)
@ -10,7 +10,7 @@
(chibi ast)
(scheme inexact)
(chibi))
(include-shared "pffi/chibi-pffi"))
(include-shared "c/lib/chibi"))
(chicken
(import (scheme base)
(scheme write)
@ -49,7 +49,7 @@
(scheme inexact)
(scheme process-context)
(gauche base)
(retropikzel pffi gauche)))
(foreign c primitives gauche)))
(gerbil
(import (scheme base)
(scheme write)
@ -293,38 +293,39 @@
;define-c-variable (?)
)
(cond-expand
(chibi (include "pffi/chibi.scm"))
(chibi (include "c/primitives/chibi.scm"))
(chicken-5 (export foreign-declare
foreign-safe-lambda
void)
(include "pffi/chicken.scm"))
(chicken-6 (include-relative "pffi/chicken.scm"))
(include "c/primitives/chicken.scm"))
(chicken-6 (include-relative "c/primitives/chicken.scm"))
(cyclone (export calculate-struct-size-and-offsets
struct-make)
(include "pffi/cyclone.scm"))
(gambit (include "pffi/gambit.scm"))
(gauche (include "pffi/gauche.scm"))
(gerbil (include "pffi/gerbil.scm"))
(guile (include "pffi/guile.scm"))
(kawa (include "pffi/kawa.scm"))
(larceny (include "pffi/larceny.scm"))
(mosh (include "pffi/mosh.scm"))
(racket (include "pffi/racket.scm"))
(sagittarius (include "pffi/sagittarius.scm"))
(skint (include "pffi/skint.scm"))
(stklos (include "pffi/stklos.scm"))
(tr7 (include "pffi/tr7.scm"))
(include "c/primitives/cyclone.scm"))
(gambit (include "c/primitives/gambit.scm"))
(gauche (include "c/primitives/gauche.scm"))
(gerbil (include "c/primitives/gerbil.scm"))
(guile (include "c/primitives/guile.scm"))
(kawa (include "c/primitives/kawa.scm"))
(larceny (include "c/primitives/larceny.scm"))
(mosh (include "c/primitives/mosh.scm"))
(racket (include "c/primitives/racket.scm"))
(sagittarius (include "c/primitives/sagittarius.scm"))
(skint (include "c/primitives/skint.scm"))
(stklos (include "c/primitives/stklos.scm"))
(tr7 (include "c/primitives/tr7.scm"))
(ypsilon (export c-function c-callback)
(include "pffi/ypsilon.scm")))
(include "c/primitives/ypsilon.scm")))
(cond-expand
(chicken-6 (include-relative "pffi/shared/main.scm")
(include-relative "pffi/shared/pointer.scm")
;(include-relative "pffi/shared/array.scm")
;(include-relative "pffi/shared/struct.scm")
(chicken-6 (include-relative "c/main.scm")
(include-relative "c/c-bytevectors.scm")
(include-relative "c/pointer.scm")
;(include-relative "c/array.scm")
;(include-relative "c/struct.scm")
)
(else (include "pffi/shared/main.scm")
;(include "pffi/shared/struct.scm")
(include "pffi/shared/c-bytevectors.scm")
(include "pffi/shared/pointer.scm")
;(include "pffi/shared/array.scm")
(else (include "c/main.scm")
;(include "c/struct.scm")
(include "c/c-bytevectors.scm")
(include "c/pointer.scm")
;(include "c/array.scm")
)))

View File

@ -1,8 +1,9 @@
CC=gcc
chibi: chibi-src/pffi.stub
chibi-ffi chibi-src/pffi.stub
${CC} -g3 -o chibi-pffi.so chibi-src/pffi.c -fPIC -lffi -shared
chibi: primitives/chibi/foreign-c.stub
chibi-ffi primitives/chibi/foreign-c.stub
mkdir -p lib
${CC} -g3 -o lib/chibi.so primitives/chibi/foreign-c.c -fPIC -lffi -shared
chicken:
@echo "Nothing to build for Chicken"
@ -13,13 +14,17 @@ cyclone:
gambit:
@echo "Nothing to build for Gambit"
gauche: gauche-src/gauche-pffi.c gauche-src/gauchelib.scm
gauche: primitives/gauche/foreign-c-primitives-gauche.c primitives/gauche/gauchelib.scm
gauche-package compile \
--srcdir=gauche-src \
--srcdir=primitives/gauche \
--cc=${CC} \
--cflags="-I./include" \
--cflags="-I./primitives/include" \
--libs=-lffi \
gauche-pffi gauche-pffi.c gauchelib.scm
foreign-c-primitives-gauche foreign-c-primitives-gauche.c gauchelib.scm
mkdir -p lib
mv foreign-c-primitives-gauche.so lib/gauche.so
mv foreign-c-primitives-gauche.o lib/gauche.o
gerbil:
@echo "Nothing to build for Gerbil"
@ -53,3 +58,7 @@ tr7:
ypsilon:
@echo "Nothing to build for Ypsilon"
clean:
@rm -rf primitives/chibi/foreign-c.c
@rm -rf lib

View File

@ -1,4 +1,4 @@
(define-module retropikzel.pffi.gauche
(define-module foreign.c.primitives.gauche
(export size-of-type
pffi-shared-object-load
c-bytevector-u8-set!
@ -14,8 +14,8 @@
define-c-procedure
define-c-callback))
(select-module retropikzel.pffi.gauche)
(dynamic-load "retropikzel/pffi/gauche-pffi")
(select-module foreign.c.primitives.gauche)
(dynamic-load "foreign/c/lib/gauche")
(define size-of-type
(lambda (type)

View File

@ -1,7 +1,7 @@
(in-module retropikzel.pffi.gauche)
(in-module foreign.c.primitives.gauche)
(inline-stub
(.include "gauche-pffi.h")
(.include "foreign-c-primitives-gauche.h")
(define-cproc size-of-int8 () size_of_int8)
(define-cproc size-of-uint8 () size_of_uint8)
(define-cproc size-of-int16 () size_of_int16)
@ -71,8 +71,8 @@
(define-cproc pointer-get-double (pointer offset::<int>) pointer_get_double)
(define-cproc pointer-get-pointer (pointer offset::<int>) pointer_get_pointer)
(define-cproc dlerror () pffi_dlerror)
(define-cproc dlsym (shared-object c-name) pffi_dlsym)
(define-cproc dlerror () internal_dlerror)
(define-cproc dlsym (shared-object c-name) internal_dlsym)
(define-cproc internal-ffi-call (nargs rtype atypes fn rvalue avalues) internal_ffi_call)
(define-cproc scheme-procedure-to-pointer (procedure) scheme_procedure_to_pointer)

View File

@ -1,16 +1,16 @@
(package
(name (retropikzel pffi))
(authors "Retropikzel")
(maintainers "Retropikzel")
(homepage "https://git.sr.ht/~retropikzel/r7rs-pffi")
(name (foreign c))
(authors "Retropikzel <retropikzel@iki.fi>")
(maintainers "Retropikzel <retropikzel@iki.fi>")
(homepage "https://git.sr.ht/~retropikzel/foreign-c")
(manual "README.md")
(description "Portable Foreign Function Interface for R7RS schemes")
(keywords 'ffi)
(license 'lgpl)
(version "0.6.0")
(version "0.10.0")
(test "test.scm")
(library
(name (retropikzel pffi))
(path "retropikzel/pffi.sld")
(name (foreign c))
(path "foreign/c.sld")
(depends)
(platforms linux windows)))

View File

@ -3,7 +3,7 @@
(scheme char)
(scheme file)
(scheme process-context)
(retropikzel pffi))
(foreign c))
;; util
(define header-count 1)

View File

@ -3,7 +3,7 @@
(scheme char)
(scheme file)
(scheme process-context)
(retropikzel pffi))
(foreign c))
(define header-count 1)

View File

@ -3,7 +3,7 @@
(scheme char)
(scheme file)
(scheme process-context)
(retropikzel pffi))
(foreign c))
;; util
(define header-count 1)