27. Bigloo A practical Scheme compiler (4.4c) User manual for version 4.4c January 2022 -- The C interface

main page
top: | Bigloo A practical Scheme compiler (4.4c) User manual for version 4.4c January 2022
The C interface
Chapters
| We call all the pieces of program devoted to the interactions between Scheme and another language a foreign interface. In Bigloo, the foreign interface allows Scheme's functions and variables to be exported to a foreign language and foreign functions and variables to be imported into the Scheme code. Using the foreign interface requires two kind of operations. Declarations --- type declarations, import declarations or export declarations. Foreign reference in the Scheme code. Declarations take place in a special module clause, see Module Declaration, and reference to foreign variables within Scheme code requires no special construction. The current release of Bigloo includes a C and a Java interface. The Java connection is specified by the means of a `java` clause (see Java Interface). The C interface is active (that is the `extern` module clauses are read) only when compiling to C. So, when compiling to Jvm the binding declared in an `extern` clause are not bound. Connecting Bigloo code with C is generally straightforward. To illustrate this simplicity, let us consider a simple example involving two source files. First a simple C file `sum.c` containing a single declaration:
    int sum(int x, int y) { return x + y; }
Then, let us assume a Bigloo source code `main.scm` that makes uses of that C function:
    (module foo
       (extern (sum::int (::int ::int) "sum"))
       (main main))

    (define (main x)
       (print (sum (length x) 10)))
With a Unix installation of Bigloo, this program can be compiled and executed with the following commands:
      $ gcc sum.c -c
      $ bigloo main.scm sum.o -o main
      $ ./main 1 2 3
The connection between Scheme and C is made particularly easy by Bigloo because the programmer is free from inserting conversion between Scheme values and C values. When needed, these are automatically inserted by the compiler.
### 27.1 The syntax of the foreign declarations
The syntax of foreign clauses is defined by:
    <extern> ==> <variable-clause>
         | <function-clause>
         | <include-clause>
         | <export-clause>
         | <type-clause>
Foreign clauses are automatically ``transmitted'' by the importation process. That is, if module `module1` imports a module `module2`, `module` treats the `extern` clauses of `module2` as though they were included in its own module declaration. Redefinition of a variable or a function already defined in an foreign clause is an error.
### 27.1.1 Automatic extern clauses generation
Extern clauses can be automatically generated using the Cigloo program which is distributed in the same package as Bigloo. Using Cigloo may be a good way to understand how C prototypes (and types) have to be declared in Bigloo. Cigloo reads C files and generates the Bigloo extern clauses for that files.
### 27.1.2 Importing an extern variable
The <variable-clause> denotes importation of variables.
    <variable-clause> ==> ( <typed-ident> <c-name>)
         | (macro <typed-ident> <string>)
         | (macro <typed-ident> (<typed-ident>+) <string>)
         | (infix macro <typed-ident> (<typed-ident>+) <string>)
Only extern ``non-`macro`'' variables are mutable (that is mutable using the `set!` construction). Bigloo does not emit ``extern C prototype'' for variables introduced by a `macro` clause. <string> is the C name of variable. The Scheme name of that variable is extracted from the <typed-ident>. Here is an example of variable importations:
    (module example
       (extern (c-var::double "c_var")
               (macro bufsiz::long "BUFSIZ")))

    (print "c-var: " c-var)
    (set! c-var (+ 1.0 c-var))
    (print "c-var: " c-var)
    (print "bufsize: " BUFSIZ)
### 27.1.3 Importing an extern function
Function are imported using the <function-clause>.
    <function-clause> ==> (<typed-ident> (<typed-ident>*) <string>)
         | (<typed-ident> (<typed-ident>+ . <typed-ident>) <string>)
         | (macro <typed-ident> (<typed-ident>*) <string>)
         | (macro <typed-ident> (<typed-ident>+ . <typed-ident>) <string>)
The function result type and Scheme name are extracted from the <typed-ident>; the <typed-ident> denotes the type of the function arguments and <string> is the C name of the function. Bigloo does not produce ``C extern prototype'' for macro functions (those introduced by `macro` clauses). If the typed identifier of the function does not contain any type information. Bigloo will emit a warning message when compiling and it will use a default C type (e.g. the `int` C type) as the return type of the function.
    (module example
       (extern (macro prn::int (::string . ::long) "printf")))

    (let ((n (read)))
       (prn #"fib(%d): %d\n" n (fib n)))
### 27.1.4 Including an extern file
C files can be included in the C code produced by using <include-clause>.
    <include-clause> ==> (include <string>)
### 27.1.5 Exporting a Scheme variable
A Scheme variable (or function) can be exported to the foreign world if and only if it is also exported using an `export` clause. Type information is given in the Scheme exportation, thus, the only requirement for a variable to be extern exported is to be given a foreign name. The foreign <export-clause> does this:
    <export-clause> ==> (export <ident> <string>)
Here is an example of exportation:
    (module example
       (export (fib::long ::long))
       (extern (export fib "scheme_fib")))

    (define (fib x) (if (< x 2) 1 ...))
### 27.1.6 Defining an extern type
New Bigloo types can be defined using extern <type-clause>. These newly introduced types can be used in any declaration (that is in any extern or Scheme module clause and in any Scheme variable or function definition). The syntax of <type-clause> is:
    <type-clause> ==> (type <ident> <type-def> <string>)
    <type-def> ==> <atomic-type>
         | <ident>
         | <struct-type>
         | <union-type>
         | <function-type>
         | <array-type>
         | <pointer-type>
         | <enum-type>
         | <opaque-type>
The symbol <ident> is the Scheme name of the introduced type and <string> is the C name of the type. When Bigloo produces the definition of a variable v of type s, it produces the following C code: s v`;`. This rules applies unless s is a pointer or an array and then, to produce a C definition, the name of the elements of the array or the elements pointed by the pointer type are used. Hence, if v is for instance `foo` and s is `(array int)` the produced C code will be: `int *foo`. #### 27.1.6.1 Atomic types The atomic types are the pre-existing ones, defined in the standard Bigloo's library.
    <atomic-type> ==> <bigloo-type>
         | <c-type>
    <bigloo-type> ==> obj procedure
         | pair | nil | pair-nil
         | bint | blong | belong | bllong
         | bignum  |real | bbool | cnst
         | bstring | ucs2string | bchar | bucs2
         | vector | tvector | struct
         | tstruct | output-port | input-port
         | binary-port | unspecified | symbol | keyword
         | cell | date | process | exit
         | mutex | condvar | mmap
         | s8vector | u8vector | s16vector | u16vector
         | s32vector | u32vector | s64vector | u64vector
         | f32vector | f64vector
         | dynamic-env | opaque | foreign
    <c-type> ==> cobj char
         | uchar | short
         | ushort | int | uint | long
         | ulong | slong | elong | llong
         | bool | string
         | file | double | float | void
         | function
The type `obj` denotes the super type of all Bigloo types (i.e., all Bigloo types, such as `procedure`, `pair`, ...) is an `obj`. The type `cobj` denotes the super of all C types (i.e., all preexisting C types such as `char`, `uchar`, `schar`, `short`, ...). The type `pair-nil` denotes values that are either pairs or the `()` value. #### 27.1.6.2 Struct and Union types C struct and Union types can be declared in Bigloo using <struct-type> clauses:
    <struct-type> ==> (struct (<typed-ident> <string>)^+)
    <union-type> ==> (union (<typed-ident> <string>)^+)
This clause declared a C struct but C structure values cannot be handled by Bigloo. Instead Bigloo is able to handle pointers to C structure. Thus, in order to help the definition of extern types, when a struct named `struct` is defined, if it does not exists yet, Bigloo automatically defines a type pointer to the structure. This type is named `struct``*`. When a pointer to a structure type is defined, Bigloo automatically produces functions to manipulate objects of this type. Let us suppose the type definition of `struct``*`:
    (type struct
       (struct (id1::type1 name1)
               ...
               (idn::typen namen))
- The following functions are created: A creator:
      (struct*::struct* ::type_1 ... ::type_n)
- This function allocates a fresh `struct``*` (in the same heap as any Scheme value) and fills the fields of the C structure with the proper values provided in the call. A type checker:
      (struct*?::bool obj::obj)
- This function returns `#t` if and only if the argument `obj` is of type `struct``*`. A null checker:
      (struct*-null?::bool ::struct*)
- This function returns `#t` if and only if its argument is `Null`. A null creator:
      (make-null-struct::struct*)
- This function creates a `NULL` value of type `struct`*. An equality checker:
      (=struct*?::bool ::struct* ::struct*)
- This function returns `#t` if and only if its arguments are equal. Accessors and mutators:
      (struct*-id_1::type_1 ::struct*)
      (struct*-id_1-set!::obj ::struct* ::type_1)
      ...
These functions read and store field values. Here is an example of structure usage:
    (module foo
       (extern
         (include "named_point_declaration.h")
         (type named-point
               (struct (x::double "x")
                       (y::double "y")
                       (name::string "name"))
               "struct named_point")
         (c-print-point::int (named-point*) "ppoint")))

    (define (scheme-print-point point)
       (print "point*-name: " point
              " x: " (named-point*-x point)
              " y: " (named-point*-y point)))

    (let ((orig (named-point* 0.0 0.0 "orig")))
       (if (named-point*-null? orig)
           (error "bigloo" "cannot allocate point" orig)
           (begin
              (c-print-point orig)
              (scheme-print-point orig))))
#### 27.1.6.3 C pointers C pointers are defined by the <pointer-type>
    <pointer-type> ==> (pointer <ident>)
<ident> is the name of a previously defined type. Let us suppose the pointer type declaration:
    (type ptr (pointer ident) ...)
- If `ident` is the name of a structure type, Bigloo automatically creates structure accessors (see C structures and unions). Otherwise, it creates the following functions: A creator:
      (make-ptr::ptr nb::long)
- This function allocates memory for `nb` elements of type `ident` and returns a `ptr` to this zone. The memory is filled with the C `Null` value. A type checker:
      (ptr?::bool obj::obj)
- This function returns `#t` the argument `obj` is of type `ptr` and `#f` otherwise. A null checker:
      (ptr-null?::bool ::ptr)
- This function returns `#t` if its argument is `Null` and `#f` otherwise. A null creator:
      (make-null-ptr::ptr*)
- This function creates a `NULL` value of type `ptr`*. An equality checker:
      (=ptr*?::bool ::ptr* ::ptr*)
- This function returns `#t` if its arguments are equal and `#f` otherwise. Accessors and mutators:
      (ptr-ref::ident ::ptr ::long)
      (ptr-set!::obj ::ptr ::long ::ident)
These functions read and store field values. Here is an example of a program using pointer types:
    (module foo
       (extern
        (type double* (pointer double) "double *")))

    (define (make-vect::double* x y z)
       (let ((vect (make-double* 3)))
          (double*-set! vect 0 x)
          (double*-set! vect 1 y)
          (double*-set! vect 2 z)
          vect))

    (define (vect-norm vect::double*)
       (sqrt (+ (expt (double*-ref vect 0) 2)
    	    (expt (double*-ref vect 1) 2)
    	    (expt (double*-ref vect 2) 2))))

    (print (vect-norm (make-vect 1.2 4.5 -4.5)))
#### 27.1.6.4 C null pointers It may be convenient to build C null pointers. Several means can be used. In particular, foreign structures and pointers are provided with Null creators. For other foreign types, the easiest one is likely to be a `pragma` form. For instance, in order to create a null pointer to a `double*` type, one may use:
    (pragma::double* "((double *)0L)")
string-ptr-null? string | bigloo procedure
void*-null? void* | bigloo procedure
These two predicates checks if there argument is the C `NULL` value.
make-string-ptr-null | bigloo procedure
make-void*-null | bigloo procedure
These two constructors creates null foreign values.
#### 27.1.6.5 C arrays C arrays are defined by the <array-type>
    <array-type> ==> (array <ident>)
<ident> is the name of a previously defined type. Array types are similar to pointer types except that they include their size in their type definition string. Let us suppose the array type declaration:
    (type array (array ident) ...)
- If `ident` is the name of a structure type, Bigloo automatically creates structures accessors (see C structures and unions). Otherwise, it creates the following functions: A creator:
      (make-array::array)
- This function allocates memory for the array `array`. The memory is filled with the C `Null` value. A type checker:
      (array?::bool obj::obj)
- This function returns `#t` if the argument `obj` is of type array and `#f` otherwise. A null checker:
      (null-array?::bool ::array)
- This function returns `#t` if the argument `obj` is `Null` and `#f` otherwise. An equality checker:
      (=array*?::bool ::array* ::array*)
- This function returns `#t` if its arguments are equal and `#f` otherwise. Accessors and mutators:
      (array-ref::ident ::array ::long)
      (array-set!::obj ::array ::long ::ident)
These functions read and store field values. Here is an example of a program using array types:
    (module foo
       (extern
        (type double* (array double) "double [ 10 ]")))

    (define (make-vect::double* x y z)
       (let ((vect (make-double*)))
          (double*-set! vect 0 x)
          (double*-set! vect 1 y)
          (double*-set! vect 2 z)
          vect))

    (define (vect-norm vect::double*)
       (sqrt (+ (expt (double*-ref vect 0) 2)
    	    (expt (double*-ref vect 1) 2)
    	    (expt (double*-ref vect 2) 2))))

    (print (vect-norm (make-vect 1.2 4.5 -4.5)))
#### 27.1.6.6 C functions C function types are introduced by the <function-type> clause:
    <function-type> ==> (function <ident> (<ident>*))
Let us suppose the array type declaration:
    (type fun (function res (arg*)) ...)
- Bigloo creates the following functions: A type checker:
      (fun?::bool obj::obj)
- This function returns `#t` if the argument `obj` is of type `fun` and `#f` otherwise. An equality checker:
      (=fun*?::bool ::fun* ::fun*)
- This function returns `#t` if and only if its arguments are equal. Caller:
      (fun-call::res f::fun a::ta ...)
This function invokes `f` with the arguments `a` ... `an`. Suppose we have to use in Scheme the following C variable:
    double (*convert)(char *);
It can be done as in:
    (module foo
       (extern
        (type *string->double
              (function double (string))
                        "double (*)(char *)")
        (macro cv::*string->double "convert")))

    (print (*string->double-call cv "3.14"))
#### 27.1.6.7 C enums This form defines `enum` types.
    <enum-type> ==> (enum (<ident> <string>)... ...)
Let us suppose the type:
    (type enum
       (enum (id_1 name_1)
             ...
             (id_n name_n)))
- Bigloo creates the following functions: Creators:
      (enum-id_1::enum)
      ...
      (enum-id_n::enum)
- These functions create `enum` values. A type checker:
      (enum?::bool obj::obj)
- This function returns `#t` if the argument `obj` is of type `enum` and `#f` otherwise. An equality checker:
      (=enum?::bool ::enum ::enum)
This function returns `#t` if the arguments are equal and `#f` otherwise. Here is an example of Scheme code using `enum` type.
    (module foo
       (extern
        (type gizmo
              (enum (titi "titi")
                    (tutu "tutu")
                    (tata "tata"))
              "enum toto")))

    (let ((v1 (gizmo-titi))
          (v2 (gizmo-tutu)))
       (print (=gizmo? v1 v2)))
#### 27.1.6.8 C opaques This form defines `opaque` types.
    <opaque-type> ==> (opaque)
Let us suppose the type:
    (type opa (opaque) ...)
- Bigloo creates the following functions: A type checker:
      (opa?::bool obj::obj)
- This function returns `#t` if the argument `obj` is of type `opa` and `#f` otherwise. An equality checker:
      (=opa?::bool ::opa ::opa)
This function returns `#t` if the arguments are equal and `#f` otherwise. Opaque types are relevant when a C value must transit via a Scheme function from a C function to another C function. The value can't be used in Scheme because no accessors are defined over that type it can only be send back to a C function. Here is an example of Scheme code using `opaque` type.
    (module foo
       (extern (type filedes (opaque) "FILE *")
               (macro _fopen::filedes (::string ::string) "fopen")
               (_fgetc::int (::filedes) "fgetc")
               (_fclose (::filedes) "fclose"))
       (export (fopen::filedes ::bstring ::bstring)
               (fclose ::filedes)
               (fgetc::char ::filedes)))

    (define (fopen fname mode)
       (_fopen fname mode))

    (define (fclose filedes)
       (_fclose filedes))

    (define (fgetc filedes)
       (integer->char (_fgetc filedes)))
Note: To illustrate the default type compilation of extern function, we have voluntary introduced an incomplete declaration for the `fclose` function. This will make Bigloo to produce a warning when compiling that module.
### 27.2 The very dangerous ``pragma'' Bigloo special forms
Bigloo has a special form which allows the inclusion of C text into the produced code. It is only applicable to the C back-end. In particular, the JVM back-end (see Chapter Java Interface) does not support it.
pragma::`ident` string [args] | bigloo syntax
free-pragma::`ident` string [args] | bigloo syntax
pragma::`ident` `ident` | bigloo syntax
This force Bigloo to include `string` in the produced C code as a regular C fragment of code. This form must not be used without an in depth understanding of Bigloo C code production; with unskilled use, the produced C file may be unacceptable to the C compiler. Values can be passed to a `pragma` form, being referenced in `string` by expressions of the form `$`number``. Such expression are replaced by the corresponding values, the number of referenced values in `string` being exactly the number of values provided. Here is an example of `pragma` usage:
    (define (fibo x::long)
       (pragma "printf( \"fib(%d):%d\\n\", $1, $2 );"
               x
               (fib x)))
Arguments provided to a pragma form are not converted during compilation. Hence, pragma arguments can be of any types, including, foreign types. A pragma result type can be specified using the notation `pragma::`name`` where the default type is `unspecified`. Then, for instance, the expression `(pragma::bool "$1 == 0" x)` will be considered to be returning a object of type `bool` (C boolean) while the expression `(pragma "$1 == 0" x)` will be considered by Bigloo to be returning the `unspecified` typed object. The compiler assumes that a `pragma` forms operates a side effects and that it writes into its parameters. This assumption no long holds with `free-pragma`. This is the only difference between the two forms. The last form `pragma `ident`` enables ``injecting'' a Scheme mangled identifier into the generated C code. Example:
    (let ((x/y 3))
      (pragma "$1 = BINT( 24 )" (pragma x/y))
      (print x/y))
### 27.3 Name mangling
In order to avoid name clashes, Bigloo uses name mangling when compiling to C or to Jvm. The name mangling for a Scheme identifier may be overridden by the means of an extern `export` clause (see Section Exporting a Scheme variable). Four public functions may be used to mangle and to demangle Scheme identifiers:
bigloo-mangle string | bigloo procedure
Mangle the identifier `string`.
bigloo-module-mangle string1 string2 | bigloo procedure
Mangle the identifier `string1` that belongs to module `string2`.
bigloo-mangled? string | bigloo procedure
Returns `#t` if `string` has been computed by the `bigloo-mangle` or `bigloo-module-mangle` function.
bigloo-class-mangled? string | bigloo procedure
Returns `#t` if `string` is a mangled name of a Bigloo class.
bigloo-need-mangling string | bigloo procedure
Returns `#t` if `string` requires name mangling because it is not a C or Jvm valid identifier.
bigloo-demangle string | bigloo procedure
Demangle previously mangled identifiers:
    (let ((id "foo!")
          (module "a-module"))
       (let ((mangled (bigloo-module-mangle id module)))
          (multiple-value-bind (new-id new-module)
             (bigloo-demangle mangled)
             (and (string=? id new-id) (string=? module new-module)))))
       => #t
bigloo-class-demangle string | bigloo procedure
Demangle previously mangled class identifier.
### 27.4 Embedded Bigloo applications
It is possible to design and realize embedded Bigloo applications. This facility is useful for adding a new Scheme part to an already existing C program. The C part of the program has only to enter the Bigloo initialization, hence, it can call any Bigloo function. Normally, Bigloo creates an initialization function called `main` when it reads a `main` module clause. To use an embedded Bigloo program, such an initialization function would have to be created but with a different name. Changing the name can be be done using the following Bigloo option: `-copt "-DBIGLOO_MAIN=<`new-name`>"`. To prevent exit from the program after <`new-name`> is executed, the following Bigloo option must be used: `-copt "-DBIGLOO_EXIT='BUNSPEC,'"`. A very important part of designing embedded Bigloo programs is being sure that all used Bigloo modules are correctly initialized and the normal way to initialize them is to use `with` clauses in the module which contains the `main` clause. An example of an embedded program can be found in the distribution's examples directory.
### 27.5 Using C bindings within the interpreter
To be able to get access to foreign functions within the Bigloo interpreter, some extra measurements have to be taken. The foreign functions have to be present in the interpreter binary, which means you have to compile a custom interpreter. Fortunately, this is easy. What has to be done is to wrap the foreign functions within Scheme and make an interpreter module. Let us consider an example where a C function `get_system_time` returning and `int` is used in an interpreter. (When linking, be sure to add the `.o` file containing the `get_system_time`.) The `ffi-interpreter.scm` file:
    (module ExtendendInterpreter
       (import (wrapper "wrapper.scm"))
       (main main))

    (define (main argv)
       (repl))
The `wrapper.scm` file:
    (module wrapper
       (extern (macro %get-system-time::int () "get_system_time"))
       (export (get-system-time))
       (eval (export-exports))

    (define (get-system-time)
      (%get-system-time))
Compile and link your application with something like:
    cc gettime.c -c gettime.o
    bigloo wrapper.scm -c
    bigloo ffi-interpreter.scm wrapper.o gettime.o

This Html page has been produced by Skribe.
Last update Tue Jan 4 08:40:10 2022.