Next: , Previous: Shared bindings between Scheme and C, Up: C interface


7.3 Calling C functions from Scheme

The external-calls structure exports several ways to call C functions from Scheme, along with several other related utilities, many of which are also available from other structures. There are two different ways to call C functions from Scheme, depending on how the C function was obtained:

— Scheme procedure: call-imported-binding binding argument ... –> value
— Scheme procedure: call-external-value byte-vector name argument ... –> value

Each of these applies its first argument, a C function, to the rest of the arguments. For call-imported-binding, the function argument must be an imported binding. For call-external-value, the function argument must be a byte vector that contains a pointer to a C function, and name should be a string that names the function. The name argument is used only for printing error messages.

For both of these, the C function is passed the argument values, and the value returned is that returned by the C function. No automatic representation conversion occurs for either arguments or return values. Up to twelve arguments may be passed. There is no method supplied for returning multiple values to Scheme from C or vice versa (mainly because C does not have multiple return values).

Keyboard interrupts that occur during a call to a C function are ignored until the function returns to Scheme.1

— Scheme syntax: import-definition name [c-string]
— Scheme syntax: import-lambda-definition name formals [c-string]

These macros simplify importing bindings from C into Scheme and wrapping such bindings in Scheme procedures. Import-definition defines name to be the shared binding named by c-string, whose value, if it is not supplied, is by default a string of name, downcased and with all hyphens translated to underscores.

          (define name (lookup-imported-binding c-string))

For example,

          (import-definition my-foo)
              ==> (define my-foo (lookup-imported-binding "my_foo"))

Import-lambda-definition imports the named C binding, using either the provided C binding name or by translating the Scheme name as with import-definition, and defines name to be a procedure with the given formal parameter list that calls the imported C binding with its arguments:

          (define binding (lookup-imported-binding c-string))
          (define (name formal ...)
            (call-imported-binding binding formal ...))

Examples:

          (import-lambda-definition integer->process-id (int)
                                    "posix_getpid")
              ==>
          (define binding0
                  (lookup-imported-binding "posix_getpid"))
          (define (integer->process-id int)
            (call-imported-binding binding0 int))
          
          (import-lambda-definition s48-system (string))
              ==>
          (define binding1
                  (lookup-imported-binding "s48_system"))
          (define (s48-system string)
            (call-imported-binding binding1 string))

where binding0 and binding1 are fresh, unused variable names.

Warning: Import-lambda-definition, as presently implemented, requires a fixed parameter list; it does not allow `rest list' arguments.

— Scheme procedure: lookup-imported-binding name –> shared-binding
— Scheme procedure: define-exported-binding shared-binding –> unspecified
— Scheme procedure: shared-binding-ref shared-binding –> value

These are identical to the procedures accessible with the same names from the shared-bindings structure.

— Scheme procedure: add-finalizer! object procedure –> unspecified

Registers procedure as the finalizer for object. When object is later about to be reclaimed by the garbage collector, procedure is applied to one argument, object. All finalizers are applied in a child of the root scheduler thread that is spawned after every garbage collection. If an error occurs in any finalizer, it will be printed to the standard error output port, and all other finalizers will be aborted before they are given a chance to run. Because of this, and the fact that finalizers are collected and run after every garbage collection, they should perform as little computation as possible. Procedure may also create new references to object elsewhere in the heap, in which case the object will not be reclaimed, but its associated finalizer will be forgotten.

Warning: Finalizers are expensive. Use sparingly.

— Scheme procedure: define-record-resumer record-type resumer –> unspecified

Identical to the procedure accessible with the same name from the record-types structure. Record resumers are often useful in working with foreign C data, which is in many cases specific to the program image within the operating system, and which cannot straightforwardly be relocated to a different address space.


Footnotes

[1] This is clearly a problem; we are working on a solution.