1. Accessing external objects
    1. foreign-code
    2. foreign-value
    3. foreign-declare
    4. define-foreign-type
    5. foreign-type-size
    6. define-foreign-variable
    7. foreign-lambda
    8. foreign-lambda*
    9. foreign-safe-lambda
    10. foreign-safe-lambda*
    11. foreign-primitive
  2. Returning large objects or chunks of memory to Scheme

Accessing external objects

foreign-code

[syntax] (foreign-code STRING ...)

Executes the embedded C/C++ code STRING ..., which should be a sequence of C statements, which are executed and return an unspecified result.

(foreign-code "doSomeInitStuff();")     =>  #<unspecified>

Code wrapped inside foreign-code may not invoke callbacks into Scheme.

foreign-value

[syntax] (foreign-value CODE TYPE)

Evaluates the embedded C/C++ expression CODE (which may be a string or symbol), returning a value of type given in the foreign-type specifier TYPE.

(print (foreign-value "my_version_string" c-string))

foreign-declare

[syntax] (foreign-declare STRING ...)

Include given strings verbatim into header of generated file.

define-foreign-type

[syntax] (define-foreign-type NAME TYPE [ARGCONVERT [RETCONVERT]])

Defines an alias for TYPE with the name NAME (a symbol). TYPE may be a type-specifier or a string naming a C type. The namespace of foreign type specifiers is separate from the normal Scheme namespace. The optional arguments ARGCONVERT and RETCONVERT should evaluate to procedures that map argument- and result-values to a value that can be transformed to TYPE:

(define-foreign-type char-vector 
  nonnull-c-string
  (compose list->string vector->list)
  (compose list->vector string->list) )

(define strlen
  (foreign-lambda int "strlen" char-vector) )

(strlen '#(#\a #\b #\c))                      ==> 3

(define memset
  (foreign-lambda char-vector "memset" char-vector char int) )

(memset '#(#_ #_ #_) #\X 3)                ==> #(#\X #\X #\X)

Foreign type-definitions are only visible in the compilation-unit in which they are defined, so use include to use the same definitions in multiple files.

foreign-type-size

[syntax] (foreign-type-size TYPE)

Returns the size of the storage required to hold values of the given foreign type TYPE. This is basically equivalent to

(foreign-value "sizeof(TYPE)" size_t)

but also handles user-defined types and allows "TYPE" to be a string, which will be given literally to the sizeof operator.

define-foreign-variable

[syntax] (define-foreign-variable NAME TYPE [STRING])

Defines a foreign variable of name NAME (a symbol). STRING should be the real name of a foreign variable or parameterless macro. If STRING is not given, then the variable name NAME will be converted to a string and used instead. All references and assignments (via set!) are modified to correctly convert values between Scheme and C representation. This foreign variable can only be accessed in the current compilation unit, but the name can be lexically shadowed. Note that STRING can name an arbitrary C expression. If no assignments are performed, then STRING doesn't even have to specify an lvalue. See that define-foreign-variable will not generate C declarations or memory allocation code; use it to include references to variables in external C code. To actually create Scheme variables visible from C, use define-external (see the Manual section on Callbacks). For example, the following code:

(import foreign)
(define-foreign-variable x double "var_x")
(print x)

will not work, because a reference to var_x will be inserted in the C code, but no declaration will be included (this can be easily verified by translating the program into C with csc -t program.scm). Changing the second line to (define-external x double 0.5) will work (and the value 0.5 will be printed).

foreign-lambda

[syntax] (foreign-lambda RETURNTYPE NAME ARGTYPE ...)

Represents a binding to an external routine. This form can be used in the position of an ordinary lambda expression. NAME specifies the name of the external procedure and should be a string or a symbol.

foreign-lambda*

[syntax] (foreign-lambda* RETURNTYPE ((ARGTYPE VARIABLE) ...) STRING ...)

Similar to foreign-lambda, but instead of generating code to call an external function, the body of the C procedure is directly given in STRING ...:

(define my-strlen
  (foreign-lambda* int ((c-string str))
    "int n = 0;
     while(*(str++)) ++n;
     C_return(n);") )

(my-strlen "one two three")             ==> 13

For obscure technical reasons you should use the C_return macro instead of the normal return statement to return a result from the foreign lambda body as some cleanup code has to be run before execution commences in the calling code.

foreign-safe-lambda

[syntax] (foreign-safe-lambda RETURNTYPE NAME ARGTYPE ...)

This is similar to foreign-lambda, but also allows the called function to call Scheme functions. See Callbacks.

foreign-safe-lambda*

[syntax] (foreign-safe-lambda* RETURNTYPE ((ARGTYPE VARIABLE)...) STRING ...)

This is similar to foreign-lambda*, but also allows the called function to call Scheme functions and allocate Scheme data-objects. See Callbacks.

foreign-primitive

[syntax] (foreign-primitive [RETURNTYPE] ((ARGTYPE VARIABLE) ...) STRING ...)

This is also similar to foreign-lambda* but the code will be executed in a primitive CPS context, which means it will not actually return, but call its continuation on exit. This means that code inside this form may allocate Scheme data on the C stack (the nursery) with C_alloc (see below). If the RETURNTYPE is omitted it defaults to void. You can return multiple values inside the body of the foreign-primitive form by calling this C function:

C_values(N + 2, C_SCHEME_UNDEFINED, C_k, X1, ...)

where N is the number of values to be returned, and X1, ... are the results, which should be Scheme data objects. When returning multiple values, the return-type should be omitted.

Returning just a single value can still be done via the C_return(...) macro.

Returning large objects or chunks of memory to Scheme

When you call a C function which needs to return quantities of data, several issues arise:

So some would advise you to just return a pointer to Scheme, use memcpy or any other function(s) which you need to get the data into Chicken-managed memory and into the desired kind of data structure, then free the C data. For this example, we are trying to return an array of doubles into an f64vector; we can accomplish that by adding a specialized copy function to the C library being integrated:

void CopyResults(double* vector) {
    memcpy(vector, bezierBuffer, totalOutputPoints * sizeof(double));
}

// The original C function which takes an array of doubles, 
// does some sort of transmogrification,
// retains a new malloc'd array of the results
// and returns the count
int GenerateResults(double* vector, int count) {
    ... 
}

and the "egg" which calls the C functions can be implemented like this:

(module memcpy-demo (input->output)
    (import chicken scheme foreign)
    (use srfi-4)

    (define CopyResults (foreign-lambda void "CopyResults" f64vector))

    (define GenerateResults (foreign-lambda integer "GenerateResults" f64vector integer))

    (define (input->output input)
        (let* ([size (GenerateResults input (f64vector-length input))] 
               [vect (make-f64vector size)])
            (printf "returned size ~a~%" size)
            (CopyResults vect)
            vect)))

The foreign-lambda takes care of the details in this case so that an f64vector allocated in the nursery can be treated as a plain old array of doubles in C (assuming your C compiler uses 64-bit values for double).

Various eggs provide other examples, and some of them do it more efficiently too, but this method is relatively clean and compact.


Previous: Interface to external functions and variables

Next: Foreign type specifiers