(foreign c)

Table of contents

  1. About
  2. Supported implementations
  3. Installation
  4. Documentation
    1. Types
      1. c-type?
      2. c-type-integer?
      3. c-type-char?
      4. c-type-float?
      5. c-type-double?
      6. c-type-signed?
      7. c-type-pointer?
      8. c-type-size
      9. c-type-size+
      10. c-type-size-
      11. c-type-size*
      12. c-type-size/
      13. c-type-align
      14. c-type-align+
      15. c-type-align-
      16. c-type-align*
      17. c-type-align/
    2. Libraries and procedures
      1. define-c-library
      2. define-c-procedure
    3. c-bytevectors
      1. make-c-bytevector
      2. c-bytevector
      3. c-bytevector?
      4. c-bytevector-free
      5. c-bytevector-null
      6. c-bytevector-null?
      7. c-bytevector-set!
      8. c-bytevector-ref
      9. bytevector->c-bytevector
      10. c-bytevector->bytevector
      11. c-bytevector-copy
      12. c-bytevector->integer
      13. integer->c-bytevector
    4. Strings
      1. string->c-bytevector
      2. with-string->c-bytevector
      3. c-bytevector->string
      4. null-byte
    5. Pass pointer by address
      1. call-with-address-of
    6. Arrays
      1. list->array
      2. array->list
      3. vector->array
      4. array->vector
    7. Structs
      1. struct-offsets
      2. struct-member-offset
      3. struct-member-type
      4. struct-size
      5. struct->list
      6. list->struct
    8. Callbacks
      1. define-c-callback
      2. pointer->c-bytevector
    9. Environment variables
      1. FOREIGN_C_LOAD_PATH
  5. Roadmap
  6. Implementation support being worked on
  7. Adding support for new implementation
  8. Notes

About

(foreign c) is a C foreign function interface (FFI) library for R6RS and R7RS Schemes

Supported implementations

R6RS is supported trough akku.

R7RS is supported trough snow-fort.

Installation

R6RS


akku install "(foreign c)"

R7RS


snow-chibi --impls=$SCHEME foreign.c

Documentation

Types

(c-type? type) lol

If type is C type returns #t otherwise #f.

Example:


(c-type? 'i8)
;> #t


(c-type-integer? type)

If type is C integer type returns #t otherwise #f.

Example:


(c-type-integer? 'i8)
;> #t


(c-type-char? type)

If type is C char type returns #t otherwise #f.

Example:


(c-type-char? 'char)
;> #t


(c-type-float? type)

If type is C float type returns #t otherwise #f.

Example:


(c-type-float? 'float)
;> #t


(c-type-double? type)

If type is C double type returns #t otherwise #f.

Example:


(c-type-double? 'double)
;> #t


(c-type-signed? type)

If type is C signed type returns #t otherwise #f.

Example:


(c-type-signed? 'i8)
;> #t
(c-type-signed? 'u8)
;> #f


(c-type-pointer? type)

If type is C pointer type returns #t otherwise #f.

Example:


(c-type-pointer? 'pointer)
;> #t


(c-type-size type)

Returns the size of type.

Example:


(c-type-size 'i8)
;> 1

(make-c-bytevector (* (c-type-size 'int)))


(c-type-size+ type ...)

Adds type sizes together and returns the result.

Example:


(c-type-size+ 'u8 'u8)
;> 2

(c-type-size+ 'int 'int)
;> 8


(c-type-size- type ...)

Subtracts types sizes and returns the result.

Example:


(c-type-size- 'int 'u8)
;> 3


(c-type-size* type n)

Multiplies type size with n and returns the result.

Example:


(c-type-size* 'int 2)
;> 8


(c-type-size/ type n)

Divides type size with n and returns the result.

Example:


(c-type-size/ 'int 2)
;> 2


(c-type-align type)

Returns the align of type.

Example:


(c-type-align 'i8)
;> 1
(c-type-align 'int)
;> 4


(c-type-align+ type ...)

Adds given types alignments together and returns the result.

Example:


(c-type-align+ 'u8 'u8)
> 2


(c-type-align- type ...)

Subtracts given aligns and returns the result.

Example:


(c-type-align- 'int 'u8)
;> 3


(c-type-align* type ...)

Multiples given align with n and returns the result.

Example:


(c-type-align* 'int 100)
;> 400


(c-type-align/ type ...)

Divides given align with n and retursn the result.

Example:


(c-type-align/ 'int 2)
;> 2


Libraries and procedures

(define-c-library scheme-name headers shared-object-name options)

Takes a scheme-name to bind the library to, list of C headers as strings, shared-object-name or #f and options. If shared-object-name is given as #f then platforms C library is used.

The C header strings should not contain "<" or ">", they are added automatically. Pass them using ' and not (list ...).

The shared-object-name should not contain suffix like .so or .dll. Nor should it contain any prefix like "lib".

Options:

Example:


(define-c-library libc '("stdio.h") #f '())

(define-c-library libcurl '("curl/curl.h")
    "curl"
    '((additional-versions ("" "0" "6"))
      (additional-paths ("."))))


(define-c-procedure scheme-name shared-object c-name return-type argument-types)}

Takes a scheme-name to bind the C procedure to, shared-object where the function is looked from, c-name of the function as symbol, return-type and argument-types.

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

Example:


(define-c-library libc '("stdlib.h") #f '())
(define-c-procedure c-puts libc 'puts 'int '(pointer))
(define str-cbv (string->c-bytevector "Message brought to you by (foreign c)!"))
(c-puts str-cb)
(c-bytevector-free str-cbv)
;> Message brought to you by (foreign c)

c-bytevectors

(make-c-bytevector size [byte])

Returns a newly allocated c-bytevector of size bytes.

If the byte argument is missing, the initial contents of the returned c-bytevector are unspecified.

If the fill argument is present, it's value must confine to C u8 values, it specifies the initial value for the bytes of the c-bytevector.

Example:


(make-c-bytevector 128)

(make-c-bytevector 128 0)

(c-bytevector byte ...) Returns a newly allocated c-bytevector containing bytes.

Example:


(c-bytevector 0 0 0 0)

(c-bytevector? obj)

Returns #t if obj is c-bytevector, otherwise returns #f.

(c-bytevector-free cbv ...)

Frees given c-bytevectors from memory.

(c-bytevector-null)

Returns a null c-bytevector.

(c-bytevector-null?)

Returns #t if obj is a null c-bytevector, otherwise returns #f.

(c-bytevector-set! cbv type offset value)

Set value of given type on offset on bytevector cbv. Offset is counted as bytes.

(c-bytevector-ref cbv type offset)

Get value of given type on offset on bytevector cbv. Offset is counted as bytes.

(bytevector->c-bytevector bv)

Returns a newly allocated c-bytevector of the bytes of bytevector bv.

(c-bytevector->bytevector cbv size)

Returns a newly allocated bytevector of the bytes of c-bytevector cbv. If size is larger than length of c-bytevector the effect is unspecified.

(c-bytevector->copy cbv [start)

Returns a newly allocated bytevector of the bytes of c-bytevector cbv. If size is larger than length of c-bytevector the effect is unspecified.

(c-bytevector->integer cbv offset)

Returns the address of the bytevector cbv as integer. If offset is given it is added to the the returned integer. offset must be an integer.

(integer->c-bytevector address)

Returns the bytevector in the integer address.

Strings

(string->c-bytevector str)

Returns a newly allocated c-bytevector that contains the (string->utf8) bytes of the given string str.

(with-string->c-bytevector str thunk) Calls thunk with newly allocated c-bytevector that contains the (utf8->string) bytes of the given string str and frees it after thunk finishes.

(c-bytevector->string cbv)

Returns a newly allocated string that contains (utf8->string) bytes of given c-bytevector cbv. If cbv is null empty string is returned.

null-byte

Null byte (\0) you can use in strings.

Pass pointer by address

(call-with-address-of cbv thunk)

Calls procedure thunk with address c-bytevector of c-bytevector cbv.

Since the support for calling C functions taking pointer address arguments, ones prefixed with & in C, varies, some additional ceremony is needed on the Scheme side.

Example:

Calling from C:


//void func(int\*\* i);
func(&i);

Calling from Scheme:


(define cbv (make-bytevector (c-type-size 'int)))
(call-with-address-of
  cbv
  (lambda (address-cbv)
    (func address-cbv)))
; Use cbv here

The passed c-bytevector, in example named cbv, should only be used after call to call-with-addres-of ends.

Arrays

To access C array index use c-bytevector-ref, c-bytevector-set! and c-type-size. For example say you have array of type 'int and you want to access the 10th index.


(c-bytevector-ref cbv (* (c-type-size 'int) 9))

; Or shortcut
(c-bytevector-ref cbv (c-type-size* 'int 9))

(list->array lst type [cbv])

Returns c-bytevector with list lst values written into it.

The list values must be/fit into C type type. If c-bytevector cbv is given, values are written into that. If not, a new c-bytevector is allocated.

Example:


(define (cbv (list->array '(1 2 3 4) 'int)))
(c-bytevector-ref cbv 'int (c-type-size* 'int 0))
;> 1
(c-bytevector-ref cbv 'int (c-type-size* 'int 1))
;> 2
(c-bytevector-ref cbv 'int (c-type-size* 'int 2))
;> 3
(c-bytevector-ref cbv 'int (c-type-size* 'int 3))
;> 4

(array->list cbv type size)

Returns list with values of c-bytevector cbv read into it.

Example:


(define cbv (list->array '(1 2 3 4) 'int))
(array->list cbv 'int 4)
;> (1 2 3 4)

(vector->array vec type [cbv])

Returns c-bytevector with vector vec values written into it.

The vector values must be/fit into C type type. If c-bytevector cbv is given, values are written into that. If not, a new c-bytevector is allocated.

(array->vector cbv type size [vec])

Returns list with values of c-bytevector cbv read into it. If vector vec is given values are read into that, if not a new vector is returned.

Structs

(struct-offsets members)

Calculates offsets of struct members for given struct member names and types. Argument must be a list of items of lists each containing two items. Name of member and it's type. Returns an association list.

Type must be C type (symbol) but name can be quite freely chosen.

Example:


(struct-offsets '((a int) (b int) (c float) (d pointer)))
;> ((a 0) (b 4) (c 8) (d 16))

(struct-offsets '(("a" int) ("b" int) ("c" float) ("d" pointer)))
;> (("a" 0) ("b" 4) ("c" 8) ("d" 16))


(define color-cbv (make-c-bytevector (* (c-type-size 'int) 4)))
(define color-offsets (struct-offsets '((r int) (g int) (b int) (a int))))
(c-bytevector-set! color-cbv 'int (cadr (assoc 'g color-offsets)) 100)
(c-bytevector-ref color-cbv 'int (cadr (assoc 'g color-offsets)))
;> 100

(struct-member-offset name offsets)

Utility function to return the offset of member name from given offsets

(struct-member-type name members)

Utility function to return the type of member name from given members

(struct-size members)

Calculates the size of struct from given struct members.

(struct->list members cbv [offsets])

Read all values from c-bytevector cbv using members. members is list of member names and types. Optionally offsets can be given, if not they are calculated from members each time procedure is called.

Example:


(define color-cbv (make-c-bytevector (* (c-type-size 'int) 4))
(define color-members '((r int) (g int) (b int) (a int)))
(define color-offsets (struct-offsets color-members))

(c-bytevector-set! color-cbv 'int (cadr (assq 'r color-offsets)) 1)
(c-bytevector-set! color-cbv 'int (cadr (assq 'g color-offsets)) 2)
(c-bytevector-set! color-cbv 'int (cadr (assq 'b color-offsets)) 3)
(c-bytevector-set! color-cbv 'int (cadr (assq 'a color-offsets)) 4)
(struct->list color-members color-cbv)
;> '((r 1) (g 2) (b 3) (a 4))
(struct->list color-members color-cbv color-offsets)
;> '((r 1) (g 2) (b 3) (a 4))

(list->struct members lst [offsets])

Write all values from list lst using members. members is list of member names and types. Optionally offsets can be given, if not they are calculated from members each time procedure is called.

Example:


(define color-members '((r int) (g int) (b int) (a int)))
(define color-offsets (struct-offsets color-members))
(define color-cbv (list->struct color-members '(4 3 2 1)))

(struct->list color-members color-cbv)
;> '((r 4) (g 3) (b 2) (a 1))

(struct->list color-members color-cbv color-offsets)
;> '((r 4) (g 3) (b 2) (a 1))

Callbacks

(define-c-callback scheme-name return-type argument-types procedure)

Defines a new Sceme function to be used as callback to C code.

Takes scheme-name to bind the Scheme procedure to, return-type, argument-types and procedure, as in place lambda!.

(pointer->c-bytevector pointer)

This procedure can be used inside the callback to make c-bytevector out of C pointer pointer.

Environment variables

FOREIGN_C_LOAD_PATH

To add more paths to where foreign c looks for libraries set FOREIGN_C_LOAD_PATH to paths separated by ; on windows, and : on other operating systems.

Roadmap

Implementation support being worked on

Adding support for new implementation

Implementation specific code

SCHEME-primitives.scm must implement:

YOURSCHEME-primitives.scm optionally can implement:

If there is no support for callbacks then this stub must be there:

(define-syntax define-c-callback
  (syntax-rules ()
    ((_ scheme-name return-type argument-types procedure)
     (define scheme-name
       (error "define-c-callback not yet supported on YOURSCHEME")))))

Run R7RS tests with:

make SCHEME=YOURSCHEME all install test

and R6RS tests with:

make SCHEME=YOURSCHEME test-r6rs

Notes