SRFI 50: Mixing Scheme and C

Title

SRFI 50: Mixing Scheme and C

Authors

Richard Kelsey and Michael Sperber

Status

This SRFI is currently in withdrawn status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-50@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

Abstract

This SRFI describes an interface for calling C functions from Scheme, calling Scheme functions from C, and allocating storage in the Scheme heap. Scheme manages stub functions in C that negotiate between the calling conventions of Scheme and C and the memory allocation policies of both worlds.

The following facilities are available for interfacing between Scheme and C:

The interface is closely based on that of Scheme 48 and scsh.

Issues

SCHEME_EXTRACT_STRING

Is it always possible to implement SCHEME_EXTRACT_STRING in the manner described? SCHEME_EXTRACT_STRINGassumes that it is always possible to return a pointer to the contents of the string. Exactly how that contents might be organized is another issue. It may not be possible to use the contents of the string in an implementation-independent fashion. Efficient portable access to the contents of strings is a difficult issue.

String/character encoding

This draft does not address the string/character encoding issue at all. Worse, it probably prescribes something that is incompatible with using Unicode or some other wide representation of characters. We don't know enough about the issues involved to speculate on how they should be addressed. They definitely should be addressed, however.

Signal handling

Signals might occur during the execution of C code. Since Scheme implementations often manage signals themselves, it is not clear how to specify what happens during such an event. This aspect of the C interface is probably best left unspecified.

Callbacks

For some Scheme implementations, it may be possible to implement calls from Scheme to C in a significantly cheaper manner if callbacks from C into Scheme are forbidden. Should there be separate versions of call-imported-c-binding and call-imported-n-ary-c-binding forbidding callbacks?

Examples

More examples will be added once the design has settled down.

Rationale

The goal of this SRFI is to facilitate interfacing C libraries with a wide variety of Scheme implementations.To this end, the following criteria were considered in the design of the interface.

Specification

Requirements

Scheme implementations supporting this SRFI should also support SRFI 9 ("Defining Record Types").

C naming conventions

The names of all of Scheme's visible C bindings begin with `scheme_' (for functions and types) or `SCHEME_' (for macros). Whenever a C name is derived from a Scheme identifier, we replace `-' with `_' and convert letters to lowercase for functions and uppercase for macros. A final `?' converted to `_p' (`_P' in C macro names). A final `!' is dropped. Thus the C macro for Scheme's pair? is SCHEME_PAIR_P and the one for set-car! is SCHEME_SET_CAR. Functions and macros that do not check the types of their arguments have `unsafe' in their names.

Prototypes

All of the C functions and macros described have prototypes or definitions in the header file srfi-50.h which can be included via

#include "srfi-50.h"
The C type for Scheme values is defined there to be scheme_value.

Note that most functionality on the C side is implemented by macros.

When the type of a value is known, such as the integer returned by vector-length or the boolean returned by pair?, the corresponding C procedure returns a C value of the appropriate type, and not a scheme_value.

Garbage collection

The Scheme system may use a copying garbage collector. The collector must be able to locate all references to objects allocated in the Scheme heap in order to ensure that storage is not reclaimed prematurely and to update references to objects moved by the collector. The garbage collector may run whenever an object is allocated in the heap. C variables whose values are Scheme objects and which are live across heap allocation calls need to be registered with the garbage collector.

Shared bindings

Shared bindings are the means by which named values are shared between Scheme code and C code. There are two separate tables of shared bindings, one for values defined in Scheme and accessed from C and the other for values going the other way. Shared bindings actually bind names to cells, to allow a name to be looked up before it has been assigned. This is necessary because C initialization code may be run before or after Scheme initialization code, where one accesses shared bindings created by the other, depending on implementation details of the underlying Scheme system.

Exporting Scheme values to C

(define-exported-c-binding name value)
scheme_value SCHEME_GET_IMPORTED_BINDING(char *name) (may GC)
scheme_value SCHEME_SHARED_BINDING_REF(scheme_value shared_binding)

Define-exported-c-binding makes value available to C code under as name which must be a string , creating a new shared binding if necessary.

The C macro SCHEME_GET_IMPORTED_BINDING returns the shared binding defined for name, again creating it if necessary.

The C macro SCHEME_SHARED_BINDING_REF dereferences a shared binding, returning its current value.

The value of a shared binding that has not been defined is undefined.

Exporting C values to Scheme

void SCHEME_DEFINE_EXPORTED_BINDING(char *name, scheme_value v) (may GC)
(get-imported-c-binding string)
(shared-c-binding-ref shared-c-binding)

These are used to define shared bindings from C and to access them from Scheme. Again, if a name is looked up before it has been defined, a new binding is created for it.

void SCHEME_EXPORT_FUNCTION(name)

This macro covers the common case of exporting a C function to Scheme. SCHEME_EXPORT_FUNCTION(name) expands into

scheme_define_exported_binding("name", scheme_enter_pointer(name))

which boxes the function into a Scheme object and then exports it. Note that scheme_enter_pointer allocates space in the Scheme heap and might trigger a garbage collection.

(import-definition <name>) (syntax)
(import-definition <name> <c-name>) (syntax)

These macros simplify importing definitions from C to Scheme. They expand into

(define <name> (get-imported-c-binding <c-name>))

where <c-name> is as supplied for the second form. For the first form <c-name> is derived from <name> by replacing `-' with `_' and converting letters to lowercase. For example, (import-definition my-foo) expands into

(define my-foo (lookup-imported-c-binding "my_foo"))

It is expected that a piece of C code that provides functions callable from Scheme provide an initialization function that creates exported bindings for these functions. The mechanism by which the Scheme system calls this initialization function is implementation-dependent.

Miscellaneous shared binding functionality

There are a number of other Scheme procedures and C functions related to shared bindings.

Scheme predicates return #t for true and #f for false. C predicates return a non-zero value for true and 0 for false.

(shared-c-binding? obj)
int SCHEME_SHARED_BINDING_P(x)

Shared-c-binding? and SCHEME_SHARED_BINDING_P are predicates for shared bindings.

(shared-c-binding-name shared-c-binding)
scheme_value SCHEME_SHARED_BINDING_NAME(scheme_value s_b)

Shared-c-binding-name and SCHEME_SHARED_BINDING_NAME return the name of a binding as a Scheme string.

(shared-c-binding-is-import? shared-c-binding)
int SCHEME_SHARED_BINDING_IS_IMPORT_P(scheme_value s_b)

Shared-c-binding-is-import? returns #t if the binding was defined from C, #f otherwise.

SCHEME_SHARED_BINDING_IS_IMPORT_P returns a non-zero value if the binding was defined from Scheme, 0 otherwise.

(shared-c-binding-set! shared-c-binding obj)
void SCHEME_SHARED_BINDING_SET(scheme_value s_b, scheme_value v)

Shared-c-binding-set! and SCHEME_SHARED_BINDING_SET change the value of a binding.

(define-imported-c-binding string obj)
(lookup-exported-c-binding string)

Define-imported-c-binding and lookup-exported-c-binding are Scheme versions of scheme_define_exported_binding and scheme_lookup_imported_binding.

(undefine-imported-c-binding string)
(undefine-exported-c-binding string)

The two undefine- procedures remove bindings from the two tables. They do nothing if the name is not found in the table.

Calling C functions from Scheme

(call-imported-c-binding binding arg1 ...)

This applies its first argument, an imported binding containing a C function, to the rest of the arguments. Binding must be an imported binding that contains a C function pointer converted to a Scheme value via SCHEME_ENTER_POINTER. The C function is passed the argi values and the value returned is that returned by C procedure. No automatic representation conversion occurs for either arguments or return values. Up to 12 arguments may be passed.

Note that the C function, if it allocates Scheme heap storage, must register its parameters with the garbage collector.

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).

(call-imported-c-binding/variable-arity binding arg1 ... argn)

This applies its first argument, an imported binding containing a C function to two arguments:

  1. the number n, represented as an int.
  2. an array of type scheme_value [] which contains the arguments arg1...argn in that order.

Upon entry into the C function, the Scheme objects contained in the array have already been registered with the garbage collector and are deregistered after the C function has returned.

The C function should not modify the contents of the array.

(import-lambda-definition <name> ( <variable> ... )) (syntax)
(import-lambda-definition <name> ( <variable> ... ) <c-name>) (syntax)

These macros simplify importing functions from C. They define <name> to be a procedure with the given parameters that passes their values to the corresponding C binding.<C-name>, if supplied, should be a string. These expand into

(define <name>
  (let ((temp (lookup-imported-c-binding <c-name>)))
    (lambda (<variable> ...)
      (call-imported-c-binding temp <variable> ...)))

If <c-name> is not supplied, it is derived from <name> by converting all letters to lowercase and replacing `-' with `_'.

Accessing Scheme data from C

Constants

The following macros denote Scheme constants:

SCHEME_FALSE
is #f.
SCHEME_TRUE
is #t.
SCHEME_NULL
is the empty list.
SCHEME_UNSPECIFIC
is a value used for functions which have no meaningful return value.

Converting values

The following macros convert values between Scheme and C representations. The `EXTRACT' ones convert from Scheme to C and the `ENTER's go the other way.

All of the `EXTRACT' macros check their arguments. Upon a type (or range) mismatch, an error is signalled. (See Signalling errors from external code.)

int SCHEME_EXTRACT_BOOLEAN(scheme_value)
scheme_value SCHEME_ENTER_BOOLEAN(int)

SCHEME_EXTRACT_BOOLEAN returns 0 if its argument is #f and a non-zero value otherwise. SCHEME_ENTER_BOOLEAN is #f if its argument is zero and #t otherwise.

SCHEME_ENTER_BOOLEAN returns SCHEME_FALSE if its argument is 0 and SCHEME_TRUE otherwise:

char SCHEME_EXTRACT_CHAR(scheme_value)
scheme_value SCHEME_ENTER_CHAR(char) (may GC)

SCHEME_EXTRACT_CHAR converts a Scheme character to a C char value. SCHEME_ENTER_CHAR performs the conversion in the opposite direction.

char * SCHEME_EXTRACT_STRING(scheme_value)
scheme_value SCHEME_ENTER_STRING(char *) (may GC)

SCHEME_EXTRACT_STRING returns a pointer to the actual storage used by the Scheme string. If this is the case, the pointer is valid only until the next garbage collection. Note that this string may not be null-terminated; SCHEME_STRING_LENGTH returns the number of characters in the string.

SCHEME_ENTER_STRING converts its argument, which must be a null-terminated C string, to a Scheme string. SCHEME_ENTER_STRING copies the characters of its arguments.

long SCHEME_EXTRACT_LONG(scheme_value)
unsigned long SCHEME_EXTRACT_UNSIGNED_LONG(scheme_value)
int SCHEME_LONG_P(scheme_value)
int SCHEME_UNSIGNED_LONG_P(scheme_value)
scheme_value SCHEME_ENTER_LONG(long) (may GC)
scheme_value SCHEME_ENTER_UNSIGNED_LONG(unsigned long) (may GC)

SCHEME_EXTRACT_LONG extracts the numerical value of its argument, which must be an exact integer, into a C long value. SCHEME_EXTRACT_UNSIGNED_LONG extracts the value into an unsigned long value.

SCHEME_LONG_P and SCHEME_UNSIGNED_LONG_P test whether its argument, which must be an exact integer, is a suitable argument for SCHEME_EXTRACT_LONG or SCHEME_EXTRACT_UNSIGNED_LONG.

Conversely, SCHEME_ENTER_LONG and SCHEME_ENTER_UNSIGNED_LONG convert a C number to a Scheme exact integer.The SCHEME_ENTER_LONG() and SCHEME_ENTER_UNSIGNED_LONG() macros may need to allocate storage when the argument is too large to fit in an immediate representation.

double SCHEME_EXTRACT_DOUBLE(scheme_value)
scheme_value SCHEME_ENTER_DOUBLE(double) (may GC)

SCHEME_EXTRACT_DOUBLE extracts the numerical value of its argument, which must be a real, into a C double value.

Conversely, SCHEME_ENTER_DOUBLE converts a C double into a Scheme real number.

void *SCHEME_EXTRACT_POINTER(scheme_value)
scheme_value SCHEME_ENTER_POINTER(void *) (may GC)

SCHEME_ENTER_POINTER creates a Scheme heap object that contains a C pointer. This is useful in conjunction with SCHEME_DEFINE_EXPORTED_BINDING for representing C functions to be called from Scheme via call-imported-c-binding.

SCHEME_EXTRACT_POINTER extracts the C pointer from a value created by SCHEME_ENTER_POINTER.

C versions of Scheme procedures

The following macros and procedures are C versions of Scheme procedures. (An alphabetically sorted table follows.

int SCHEME_EQ_P(scheme_value, scheme_value)

is the C version of eq?.

int SCHEME_EOF_OBJECT_P(scheme_value, scheme_value)

is the C version of eof-object?.

int SCHEME_CHAR_P(scheme_value)

is the C version of char?.

int SCHEME_INTEGER_P(scheme_value)

is the C version of integer?.

int SCHEME_RATIONAL_P(scheme_value)

is the C version of rational?.

scheme_value SCHEME_NUMERATOR(scheme_value) (may GC)

is the C version of numerator.

scheme_value SCHEME_DENOMINATOR(scheme_value) (may GC)

is the C version of denominator.

int SCHEME_REAL_P(scheme_value)

is the C version of real?.

scheme_value SCHEME_MAKE_RECTANGULAR(scheme_value, scheme_value) (may GC)
scheme_value SCHEME_MAKE_POLAR(scheme_value, scheme_value) (may GC)

These are the C equivalents of the make-rectangular and make-polar Scheme procedures.

scheme_value SCHEME_MAKE_RATIONAL(scheme_value, scheme_value) (may GC)

SCHEME_MAKE_RATIONAL creates a rational number from two exact integers. The first argument is the numerator, the second argument is the denominator.

int SCHEME_COMPLEX_P(scheme_value)

is the C version of complex?.

scheme_value SCHEME_REAL_PART(scheme_value) (may GC)

is the C version of real-part.

scheme_value SCHEME_IMAG_PART(scheme_value) (may GC)

is the C version of imag-part.

scheme_value SCHEME_MAGNITUDE(scheme_value) (may GC)

is the C version of magnitude.

scheme_value SCHEME_ANGLE(scheme_value) (may GC)

is the C version of angle.

int SCHEME_NUMBER_P(scheme_value)

is the C version of number?.

scheme_value SCHEME_EXACT_P(scheme_value)

is the C version of exact?.

int SCHEME_PAIR_P(scheme_value)

is the C version of pair?.

scheme_value SCHEME_CAR(scheme_value)

is the C version of car.

scheme_value SCHEME_CDR(scheme_value)

is the C version of cdr.

void SCHEME_SET_CAR(scheme_value, scheme_value)

is the C version of set-car!.

void SCHEME_SET_CDR(scheme_value, scheme_value)

is the C version of set-cdr!.

scheme_value SCHEME_CONS(scheme_value, scheme_value) (may GC)

is the C version of cons.

int SCHEME_VECTOR_P(scheme_value)

is the C version of vector?.

long SCHEME_VECTOR_LENGTH(scheme_value)

is the C version of vector-length.

scheme_value SCHEME_VECTOR_REF(scheme_value, long)

is the C version of vector-ref.

void SCHEME_VECTOR_SET(scheme_value, long, scheme_value)

is the C version of vector-set!.

scheme_value SCHEME_MAKE_VECTOR(long, scheme_value) (may GC)

is the C version of make-vector.

int SCHEME_STRING_P(scheme_value)

is the C version of string?.

long SCHEME_STRING_LENGTH(scheme_value)

is the C version of string-length.

char SCHEME_STRING_REF(scheme_value, long)

is the C version of string-ref.

void SCHEME_STRING_SET(scheme_value, long, char)

is the C version of string-set!.

scheme_value SCHEME_MAKE_STRING(long, char) (may GC)

is the C version of make-string.

int SCHEME_SYMBOL_P(scheme_value)

is the C version of symbol?.

scheme_value SCHEME_SYMBOL_TO_STRING(scheme_value) (may GC)

is the C version of symbol->string.

Scheme versionC versionCan GC?
angleSCHEME_ANGLEyes
carSCHEME_CARno
cdrSCHEME_CDRno
char?SCHEME_CHAR_Pno
complex?SCHEME_COMPLEX_Pno
consSCHEME_CONSyes
denominatorSCHEME_DENOMINATORyes
eof-object?SCHEME_EOF_OBJECT_Pno
eq?SCHEME_EQ_Pno
exact?SCHEME_EXACT_Pno
imag-partSCHEME_IMAG_PARTyes
integer?SCHEME_INTEGER_Pno
make-polarSCHEME_MAKE_POLARyes
make-rationalSCHEME_MAKE_RATIONALyes
make-rectangularSCHEME_MAKE_RECTANGULARyes
make-stringSCHEME_MAKE_STRINGyes
make-vectorSCHEME_MAKE_VECTORyes
number?SCHEME_NUMBER_Pno
numeratorSCHEME_NUMERATORyes
pair?SCHEME_PAIR_Pno
rational?SCHEME_RATIONAL_Pno
real?SCHEME_REAL_Pno
real-partSCHEME_REAL_PARTyes
set-car!SCHEME_SET_CARno
set-cdr!SCHEME_SET_CDRno
string?SCHEME_STRING_Pno
string-lengthSCHEME_STRING_LENGTHno
string-refSCHEME_STRING_REFno
string-set!SCHEME_STRING_SETno
symbol->stringSCHEME_SYMBOL_TO_STRINGyes
vector?SCHEME_VECTOR_Pno
vector-refSCHEME_REFno
vector-set!SCHEME_SETno

Calling Scheme procedures from C

External code can call back to Scheme procedures using the following macro.

scheme_value SCHEME_CALL(scheme_value p, long nargs, ...) (may GC)

This calls the Scheme procedure p on nargs arguments, which are passed as additional arguments to scheme_call. There may be at most twelve arguments. The value returned by the Scheme procedure is returned by the C procedure. Invoking any Scheme procedure may potentially cause a garbage collection.

There are some complications that occur when mixing calls from C to Scheme with continuations and threads. C only supports downward continuations (via longjmp()). Scheme continuations that capture a portion of the C stack have to follow the same restriction. For example, suppose Scheme procedure s0 captures continuation a and then calls C procedure c0, which in turn calls Scheme procedure s1. Procedure s1 can safely call the continuation a, because that is a downward use. When a is called Scheme will remove the portion of the C stack used by the call to c0. On the other hand, if s1 captures a continuation, that continuation cannot be used from s0, because by the time control returns to s0 the C stack used by c0 will no longer be valid. An attempt to invoke an upward continuation that is closed over a portion of the C stack will raise an exception.

In Scheme, if threads are implemented using continuations, the downward restriction applies to them as well. An attempt to return from Scheme to C at a time when the appropriate C frame is not on top of the C stack will cause the current thread to block until the frame is available. For example, suppose thread t0 calls a C procedure which calls back to Scheme, at which point control switches to thread t1, which also calls C and then back to Scheme. At this point both t0 and t1 have active calls to C on the C stack, with t1's C frame above t0's. If thread t0 attempts to return from Scheme to C it will block, as its frame is not accessible. Once t1 has returned to C and from there to Scheme, t0 will be able to resume. The return to Scheme is required because context switches can only occur while Scheme code is running. T0 will also be able to resume if t1 uses a continuation to throw past its call to C.

Interacting with the Scheme heap

Any procedure that allocates objects within the Scheme heap may trigger a garbage collection. Variables bound to values in the Scheme heap need to be registered with the garbage collector so that the value will be retained and so that the variables will be updated if the garbage collector moves the object. The garbage collector has no facility for updating pointers to the interiors of objects. Consequently, such pointers, such as the ones returned by EXTRACT_STRING, will likely become invalid when a garbage collection occurs.

Registering objects with the Garbage Collector

A set of macros are used to manage the registration of automatic variables and parameters with the garbage collector.

SCHEME_DECLARE_GC_PROTECT(<n>)

SCHEME_DECLARE_GC_PROTECT(<n>), where 1 <= <n> < = 12, expands into a declaration which allocates storage for registering <n> variables. At most one use of SCHEME_DECLARE_GC_PROTECT may occur in a block.

SCHEME_GC_PROTECT_<n>(<scheme_value1>, ..., <scheme_valuen>)

This expands into a statement which registers the <n> variables (lvalues) with the garbage collector. It must be within scope of a SCHEME_DECLARE_GC_PROTECT(n) and be before any code which can cause a GC.

Note that the variables to be protected must be initialized. (With SCHEME_UNSPECIFIC, for instance.)Otherwise, program behavior is unspecified.

SCHEME_GC_UNPROTECT()

SCHEME_GC_UNPROTECT expands into a statement which removes the block's protected variables from the garbage collector's list. It must be called at the end of the block after any code which may cause a garbage collection. Omitting any of the three may cause serious and hard-to-debug problems. Notably, the garbage collector may relocate protected objects and invalidate scheme_value variables which are not protected.

It is an error when a C procedure returns to Scheme, the calls to SCHEME_GC_PROTECT() have not been matched by an equal number of calls to SCHEME_GC_UNPROTECT().

Global variables may also be registered with the garbage collector.

SCHEME_GC_PROTECT_GLOBAL(<value>)

SCHEME_GC_PROTECT_GLOBAL expands into a statement which permanently registers the variable <value> (an l-value) with the garbage collector.

SCHEME_GC_UNPROTECT_GLOBAL(<value>)

SCHEME_GC_UNPROTECT_GLOBAL expands into a statement which deregisters the variable <value> (an l-value) with the garbage collector.

Keeping C data structures in the Scheme heap

The following macros can be used to create and access C objects embedded inside Scheme objects:

scheme_value SCHEME_MAKE_VALUE(type) (may GC)
type SCHEME_EXTRACT_VALUE(scheme_value, type)
type * SCHEME_EXTRACT_VALUE_POINTER(scheme_value, type)
void SCHEME_SET_VALUE(scheme_value, type, value)
scheme_value SCHEME_MAKE_AND_SET_VALUE(type, value) (may GC)

SCHEME_MAKE_VALUE makes a Scheme object large enough to hold an object whose C type is type. SCHEME_EXTRACT_VALUE returns the contents of such an object cast to type, and SCHEME_EXTRACT_VALUE_POINTER returns a pointer to the contents of such an object. The value returned by SCHEME_EXTRACT_VALUE_POINTER is valid only until the next garbage collection.

SCHEME_SET_VALUE stores value into an object created by SCHEME_MAKE_VALUE.

SCHEME_MAKE_AND_SET_VALUE combines SCHEME_MAKE_VALUE and SCHEME_SET_VALUE.

C code and heap images

Some Scheme systems use dumped heap images to restore a previous system state. Such a scheme heap may be written into a file in a machine-independent and operating-system-independent format. The procedures described above may be used to create objects in the Scheme heap that contain information specific to the current machine, operating system, or process. A heap image containing such objects may not work correctly when resumed.

To address this problem, a record type may be given a `resumer' procedure. On startup, the resumer procedure for a type is applied to each record of that type in the image being restarted. This procedure can update the record in a manner appropriate to the machine, operating system, or process used to resume the image.

(define-record-resumer <record-type> <procedure>) (syntax)

Define-record-resumer defines <procedure>, which should accept one argument, to be the resumer for <record-type>. The order in which resumer procedures are called is not specified.

The <procedure> argument to define-record-resumer may be #f, in which case records of the given type are not written out in heap images.

When writing a heap image which would contain a reference to such a record, the Scheme implementation may refuse to write the image. If it does write the image, the effects of loading it back in are undefined.

Using Scheme records in C code

External modules can create SRFI 9 records and access their slots positionally.

scheme_value SCHEME_MAKE_RECORD(scheme_value) (may GC)
int SCHEME_RECORD_P(scheme_value)
scheme_value SCHEME_RECORD_HAS_TYPE_P(scheme_value record, scheme_value record_type)
int SCHEME_CHECK_RECORD_TYPE(scheme_value, scheme_value int)
scheme_value SCHEME_RECORD_REF(scheme_value, long)
void SCHEME_RECORD_SET(scheme_value, long, scheme_value)

The argument to SCHEME_MAKE_RECORD should be a shared binding whose value is a record type. In C, the fields of Scheme records are only accessible via offsets, with the first field having offset zero, the second offset one, and so forth. If the order of the fields is changed in the Scheme definition of the record type the C code must be updated as well.

Note that the variables a and b must be protected against the possibility of a garbage collection occuring during the call to SCHEME_MAKE_RECORD().

Signalling errors from external code

The following macros explicitly signal certain errors, and immediately return to Scheme. Signalling an error performs all necessary clean-up actions to properly return to Scheme, including adjusting the stack of protected variables. Besides that, the actual effect of signalling an error is undefined. (It is expected that a future SRFI will deal with the issue of handling error situations resulting from bugs in the program.

void SCHEME_ARITY_ERROR(int min, int max)

This signals that Scheme tried to call a C procedure with the wrong number of arguments. The min and max arguments denote the minimum and maximum number that the function could accept, respectively.

void SCHEME_ARGUMENT_TYPE_ERROR(int arg_pos, char *expected_explanation)

This signals that Scheme passed an argument of a wrong type to a C procedure. The arg_pos argument specifies the position of the invalid argument (0-based). The expected_explanation text contains a human-readable description of the type the function did actually expect.

Note to implementors: The C function is not required to provide non-local storage for the explanation. Therefore, unwinding the stack may destroy the string.

void SCHEME_OUT_OF_MEMORY_ERROR(void)

This signals that the C function ran out of memory.

void SCHEME_CHECK_BOOLEAN(scheme_value, int)
void SCHEME_CHECK_SYMBOL(scheme_value, int)
void SCHEME_CHECK_PAIR(scheme_value, int)
void SCHEME_CHECK_VECTOR(scheme_value, int)
void SCHEME_CHECK_STRING(scheme_value, int)
void SCHEME_CHECK_CHAR(scheme_value, int)
void SCHEME_CHECK_INTEGER(scheme_value, int)
void SCHEME_CHECK_RATIONAL(scheme_value, int)
void SCHEME_CHECK_REAL(scheme_value, int)
void SCHEME_CHECK_COMPLEX(scheme_value, int)
void SCHEME_CHECK_NUMBER(scheme_value, int)
void SCHEME_CHECK_RECORD(scheme_value, int)
void SCHEME_CHECK_SHARED_BINDING(scheme_value, int)

These macros raise argument type errors if their first argument does not have the required type. The second argument is the argument position passed to scheme_argument_type_error in the case of an error.

SCHEME_CHECK_BOOLEAN raises an error if its argument is neither #t or #f.

Unsafe macros

All of the C procedures and macros described above check that their arguments have the appropriate types and that indexes are in range. The following procedures and macros are identical to those described above, except that they do not perform type and range checks. They are provided for the purpose of writing more efficient code; their general use is not recommended. If a program supplies a value of the wrong type to one of these macros, program behavior is undefined.

char SCHEME_UNSAFE_EXTRACT_CHAR(scheme_value)
char * SCHEME_UNSAFE_EXTRACT_STRING(scheme_value)
long SCHEME_UNSAFE_EXTRACT_LONG(scheme_value)
unsigned long SCHEME_UNSAFE_EXTRACT_UNSIGNED_LONG(scheme_value)
double SCHEME_UNSAFE_EXTRACT_DOUBLE(scheme_value)
scheme_value SCHEME_UNSAFE_NUMERATOR(scheme_value) (may GC)
scheme_value SCHEME_UNSAFE_DENOMINATOR(scheme_value) (may GC)
scheme_value SCHEME_UNSAFE_REAL_PART(scheme_value) (may GC)
scheme_value SCHEME_UNSAFE_IMAG_PART(scheme_value) (may GC)
scheme_value SCHEME_UNSAFE_MAGNITUDE(scheme_value) (may GC)
scheme_value SCHEME_UNSAFE_ANGLE(scheme_value) (may GC)
scheme_value SCHEME_UNSAFE_CAR(scheme_value)
scheme_value SCHEME_UNSAFE_CDR(scheme_value)
void SCHEME_UNSAFE_SET_CAR(scheme_value, scheme_value)
void SCHEME_UNSAFE_SET_CDR(scheme_value, scheme_value)
long SCHEME_UNSAFE_VECTOR_LENGTH(scheme_value)
scheme_value SCHEME_UNSAFE_VECTOR_REF(scheme_value, long)
void SCHEME_UNSAFE_VECTOR_SET(scheme_value, long, scheme_value)
long SCHEME_UNSAFE_STRING_LENGTH(scheme_value)
char SCHEME_UNSAFE_STRING_REF(scheme_value, long)
void SCHEME_UNSAFE_STRING_SET(scheme_value, long, char)
scheme_value SCHEME_UNSAFE_SYMBOL_TO_STRING(scheme_value) (may GC)
scheme_value SCHEME_UNSAFE_SHARED_BINDING_REF(scheme_value s_b)
int SCHEME_UNSAFE_SHARED_BINDING_P(x)
int SCHEME_UNSAFE_SHARED_BINDING_IS_IMPORT_P(scheme_value s_b)
scheme_value SCHEME_UNSAFE_SHARED_BINDING_NAME(scheme_value s_b)
void SCHEME_UNSAFE_SHARED_BINDING_SET(scheme_value s_b, scheme_value value)
scheme_value SCHEME_UNSAFE_RECORD_TYPE(scheme_value)
scheme_value SCHEME_UNSAFE_RECORD_REF(scheme_value, long)
void SCHEME_UNSAFE_RECORD_SET(scheme_value, long, scheme_value)
type SCHEME_UNSAFE_EXTRACT_VALUE(scheme_value, type)
type * SCHEME_UNSAFE_EXTRACT_VALUE_POINTER(scheme_value, type)
void SCHEME_UNSAFE_SET_VALUE(scheme_value, type, value)

Examples

Creating records in C

Given the following SRFI 9 record-type definition

(define-record-type :thing
  (make-thing a b)
  thing?
  (a thing-a)
  (b thing-b))

the identifier :thing is bound to the record type and can be exported to C:

(define-exported-c-binding "thing-record-type" :thing)

Thing records can then be made in C:

static scheme_value
  thing_record_type_binding = SCHEME_UNSPECIFIC;

void initialize_things(void)
{
  SCHEME_GC_PROTECT_GLOBAL(thing_record_type_binding);
  thing_record_type_binding =
     SCHEME_GET_IMPORTED_BINDING("thing-record-type");
}

scheme_value make_thing(scheme_value a, scheme_value b)
{
  scheme_value thing;
  SCHEME_DECLARE_GC_PROTECT(2);

  SCHEME_GC_PROTECT_2(a, b);

  thing = SCHEME_MAKE_RECORD(thing_record_type_binding);
  SCHEME_RECORD_SET(thing, 0, a);
  SCHEME_RECORD_SET(thing, 1, b);

  SCHEME_GC_UNPROTECT();

  return thing;
}

References

Copyright

Copyright (C) Richard Kelsey and Michael Sperber (2003). All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Editor: David Rush