foreigners

  1. foreigners
    1. Synopsis
    2. Interface
      1. define-foreign-record-type
        1. Slot definitions
        2. The getter
        3. The setter
        4. record declarations
          1. constructor
          2. destructor
      2. define-foreign-enum-type
        1. enumspec
    3. Examples
      1. define-foreign-enum-type
      2. define-foreign-record-type
    4. Author
    5. Version history
    6. License

Synopsis

foreigners is a collection of useful FFI macros.

To use this extension, import both the core FFI syntax and the syntax we provide:

(import foreign)     ; core FFI syntax
(import foreigners)  ; this egg

Interface

define-foreign-record-type

[syntax] (define-foreign-record-type name [decl ...] slot ...)

This macro defines accessor procedures for a C structure definition. It is the counterpart to Chicken 3's define-foreign-record, but modeled after SRFI 9's define-record-type for better hygiene.

NAME should either be a symbol or a list of the form (TYPENAME FOREIGNNAME). If NAME is a symbol, then a C declaration will be generated that defines a C struct named struct NAME. If NAME is a list, then no struct declaration will be generated and FOREIGNNAME should name an existing C record type. A foreign-type specifier named NAME (or TYPENAME) will be defined as a pointer to the given C structure.

Slot definitions

A SLOT definition should be a list of one of the following forms:

(type slotname getter [setter])
(type (slotname size) getter [setter])

where TYPE, SLOTNAME, GETTER and SETTER are all symbols. The latter form defines an array of SIZE elements of the type TYPE embedded in the structure. For every slot, a getter procedure and optionally a setter procedure will be generated.

The getter
(getter foreign-record-pointer [index])

A procedure of one argument (a pointer to a C structure), that returns the slot value of the slot SLOTNAME. If a SIZE has been given in the slot definition, then an additional argument INDEX is required that specifies the index of an array-element.

The setter
(setter foreign-record-pointer [index] value)

A procedure of two arguments (a pointer to a C structure) and a value, that sets the slot value of the slot SLOTNAME in the structure. If a SIZE has been given in the slot definition, then an additional argument INDEX is required for the array index.

If the setter is omitted from the slot form, no setter procedure will be generated. This is the equivalent of the (const ...) modifier in Chicken 3. Slots of the types (struct ...) or (union ...) are accessed as pointers to the embedded struct (or union) and no setter will be generated regardless.

record declarations

Additionally, special record declarations (DECL ...) may be given, where each declaration consists of a list of the form (KEYWORD ARGUMENT ...). Declarations must occur before slots. The available declarations are:

constructor
(constructor: NAME)

Generate a constructor-procedure with no arguments that has the name NAME (a symbol) that returns a pointer to a structure of this type. The storage will be allocated with malloc(3).

Memory allocated by a constructor is not automatically freed. You will probably need to generate a destructor (see below) and call it manually, or register a finalizer yourself.

destructor
(destructor: NAME)

Generate a destructor function with the name NAME that takes a pointer to a structure of this type as its single argument and releases the storage with free(3). If the argument is #f, the destructor procedure does nothing.

define-foreign-enum-type

[syntax] (define-foreign-enum-type (type-name native-type [default-value]) (scheme->number number->scheme) enumspec ...)

Defines a foreign-type that maps the elements of a C/C++ enum (or a enum-like list of constants) to and from a set of symbols. Also defines a pair of conversion procedures which can be used to do this mapping manually, along with a foreign-variable for each enumeration value.

The two procedures are defined by the macro in lexical scope for your use, and are also used in the foreign-type conversion:

(define-foreign-type type-name native-type scheme->number number->scheme)

If passed a list of symbols, scheme->number will combine their numeric values with bitwise-ior. However, number->scheme returns a single symbol and will not decompose a value into its component symbols; rather, you will probably get default-value.

enumspec

The most general form of enumspec is ((symbol var-name) native-name destination-value). Some of these items can be omitted; see below. For each of these forms, a foreign-variable is generated:

(define-foreign-variable var-name native-type native-name)

Furthermore, forward and reverse mappings are added:

  {{scheme->number}}: from {{symbol}} to the foreign value {{native-name}}; also accepts {{symbol}}'s keyword equivalent
  {{number->scheme}}: from {{native-name}} to {{destination-value}}

Normally, destination-value is omitted from the spec, and the symbol is used as the destination value, making the mapping reversible. But occasionally, overriding the value can be useful, for example in changing a zero return value to #f.

If native-name is omitted, symbol is used --- useful if you want your Scheme symbols to reflect the underlying constant names. If var-name is omitted, a temporary generated symbol is used, so the foreign-variable will not be visible to your compilation unit.

Stating the enumspec rules explicitly:

((s v) n d)                  ; general form
((s v) n)   -> ((s v) n s)
((s) n d)   -> ((s t) n d)   ; t is a temporary name (gensym)
((s) n)     -> ((s t) n s)
(s n d)     -> ((s s) n d)
(s n)       -> ((s s) n s)
s           -> ((s s) s s)

Examples

define-foreign-enum-type

Example of a module which uses define-foreign-enum-type:

#>
#define STATUS_OK 0
#define STATUS_BUSY 1
#define STATUS_FAIL 2

static int _status = STATUS_OK;
int get_status() { return _status; }
void set_status(int s) { _status = s; }
<#

(module enum-1
  (status->int int->status
   get-status set-status busy?)

  (import scheme chicken foreigners)

  (define-foreign-enum-type (status int)
    (status->int int->status)
    ((ok status/ok) STATUS_OK)
    ((busy status/busy) STATUS_BUSY)
    ((fail status/fail) STATUS_FAIL))

  (define get-status
    (foreign-lambda status "get_status"))
  (define set-status
    (foreign-lambda void "set_status" status))
  (define (busy? s)
    (= s status/busy))
  )

This may be tested with:

(require-extension test)
(require-extension enum-1)

(test "status->int" 1 (status->int 'busy))
(test "int->status" 'fail (int->status 2))
(set-status 'busy)
(test "get-status" 'busy (get-status))
(test "status/busy via busy?" #t (busy? (status->int (get-status))))

define-foreign-record-type

Example that wraps the servent structure --- see getservent(3).

 #>
 struct servent {
   char  *s_name;
   char **s_aliases;
   int    s_port;
   char  *s_proto;
 };                 <#

(define-foreign-type port-number int      ; used by servent type
  (foreign-lambda int "htons" int)
  (foreign-lambda int "ntohs" int) )

(define-foreign-record-type (servent "struct servent")
  (constructor: make-servent)
  (destructor: free-servent)
  (c-string s_name servent-name servent-name-set!)
  (c-pointer s_aliases servent-s_aliases)              ; const
  (port-number s_port servent-port servent-port-set!)
  (c-string s_proto servent-proto servent-proto-set!))

Author

Jim Ursetto

Version history

License

BSD.