On this page:
1.1 “Scheme” versus “Racket”
1.2 CGC versus 3m
1.3 Writing Racket Extensions
1.3.1 CGC Extensions
1.3.2 3m Extensions
1.3.3 Declaring a Module in an Extension
1.4 Embedding Racket into a Program
1.4.1 CGC Embedding
1.4.2 3m Embedding
1.5 Racket and Threads
1.6 Racket, Unicode, Characters, and Strings
1.7 Integers

1 Overview

1.1 “Scheme” versus “Racket”

The old name for Racket was “PLT Scheme,” and the core compiler and run-time system used to be called “MzScheme.” The old names are entrenched in Racket internals, to the point that most C bindings defined in this manual start with scheme_. They all should be renamed to start racket_.

1.2 CGC versus 3m

Before mixing any C code with Racket, first decide whether to use the 3m variant of Racket, the CGC variant of Racket, or both:

At the C level, working with CGC can be much easier than working with 3m, but overall system performance is typically better with 3m.

1.3 Writing Racket Extensions

The process of creating an extension for 3m or CGC is essentially the same, but the process for 3m is most easily understood as a variant of the process for CGC.

1.3.1 CGC Extensions

To write a C/C++-based extension for Racket CGC, follow these steps:

IMPORTANT: With Racket CGC, Racket values are garbage collected using a conservative garbage collector, so pointers to Racket objects can be kept in registers, stack variables, or structures allocated with scheme_malloc. However, static variables that contain pointers to collectable memory must be registered using scheme_register_extension_global (see Memory Allocation).

As an example, the following C code defines an extension that returns "hello world" when it is loaded:

  #include "escheme.h"

  Scheme_Object *scheme_initialize(Scheme_Env *env) {

    return scheme_make_utf8_string("hello world");

  }

  Scheme_Object *scheme_reload(Scheme_Env *env) {

    return scheme_initialize(env); /* Nothing special for reload */

  }

  Scheme_Object *scheme_module_name() {

    return scheme_false;

  }

Assuming that this code is in the file "hw.c", the extension is compiled under Unix with the following two commands:

  raco ctool --cgc --cc hw.c
  raco ctool --cgc --ld hw.so hw.o

(Note that the --cgc, --cc, and --ld flags are each prefixed by two dashes, not one.)

The "collects/mzscheme/examples" directory in the Racket distribution contains additional examples.

1.3.2 3m Extensions

To build an extension to work with Racket 3m, the CGC instructions must be extended as follows:

For a relatively simple extension "hw.c", the extension is compiled under Unix for 3m with the following three commands:

  raco ctool --xform hw.c
  raco ctool --3m --cc hw.3m.c
  raco ctool --3m --ld hw.so hw.o

Some examples in "collects/mzscheme/examples" work with Racket 3m in this way. A few examples are manually instrumented, in which case the --xform step should be skipped.

1.3.3 Declaring a Module in an Extension

To create an extension that behaves as a module, return a symbol from scheme_module_name, and have scheme_initialize and scheme_rename declare a module using scheme_primitive_module.

For example, the following extension implements a module named hello that exports a binding greeting:

  #include "escheme.h"

  

  Scheme_Object *scheme_initialize(Scheme_Env *env) {

    Scheme_Env *mod_env;

    mod_env = scheme_primitive_module(scheme_intern_symbol("hi"),

                                      env);

    scheme_add_global("greeting",

                      scheme_make_utf8_string("hello"),

                      mod_env);

    scheme_finish_primitive_module(mod_env);

    return scheme_void;

  }

  

  Scheme_Object *scheme_reload(Scheme_Env *env) {

    return scheme_initialize(env); /* Nothing special for reload */

  }

  

  Scheme_Object *scheme_module_name() {

    return scheme_intern_symbol("hi");

  }

This extension could be compiled for 3m on i386 Linux, for example, using the following sequence of mzc commands:

  raco ctool --xform hi.c
  raco ctool --3m --cc hi.3m.c
  mkdir -p compiled/native/i386-linux/3m
  raco ctool --3m --ld compiled/native/i386-linux/3m/hi_rkt.so hi_3m.o

The resulting module can be loaded with

(require "hi.rkt")

1.4 Embedding Racket into a Program

Like creating extensions, the embedding process for Racket CGC or Racket 3m is essentially the same, but the process for Racket 3m is most easily understood as a variant of the process for Racket CGC.

1.4.1 CGC Embedding

To embed Racket CGC in a program, follow these steps:

With Racket CGC, Racket values are garbage collected using a conservative garbage collector, so pointers to Racket objects can be kept in registers, stack variables, or structures allocated with scheme_malloc. In an embedding application on some platforms, static variables are also automatically registered as roots for garbage collection (but see notes below specific to Mac OS X and Windows).

For example, the following is a simple embedding program which evaluates all expressions provided on the command line and displays the results, then runs a read-eval-print loop. Run

  raco ctool --c-mods base.c ++lib racket/base

to generate "base.c", which encapsulates racket/base and all of its transitive imports (so that they need not be found separately a run time).

  #include "scheme.h"

  

  #include "base.c"

  

  static int run(Scheme_Env *e, int argc, char *argv[])

  {

    Scheme_Object *curout;

    int i;

    mz_jmp_buf * volatile save, fresh;

  

    /* Declare embedded modules in "base.c": */

    declare_modules(e);

  

    scheme_namespace_require(scheme_intern_symbol("racket/base"));

  

    curout = scheme_get_param(scheme_current_config(),

                              MZCONFIG_OUTPUT_PORT);

  

    for (i = 1; i < argc; i++) {

      save = scheme_current_thread->error_buf;

      scheme_current_thread->error_buf = &fresh;

      if (scheme_setjmp(scheme_error_buf)) {

        scheme_current_thread->error_buf = save;

        return -1; /* There was an error */

      } else {

        Scheme_Object *v, *a[2];

        v = scheme_eval_string(argv[i], e);

        scheme_display(v, curout);

        scheme_display(scheme_make_char('\n'), curout);

        /* read-eval-print loop, uses initial Scheme_Env: */

        a[0] = scheme_intern_symbol("racket/base");

        a[1] = scheme_intern_symbol("read-eval-print-loop");

        scheme_apply(scheme_dynamic_require(2, a), 0, NULL);

        scheme_current_thread->error_buf = save;

      }

    }

    return 0;

  }

  

  int main(int argc, char *argv[])

  {

    return scheme_main_setup(1, run, argc, argv);

  }

Under Mac OS X, or under Windows when Racket is compiled to a DLL using Cygwin, the garbage collector cannot find static variables automatically. In that case, scheme_main_setup must be called with a non-zero first argument.

Under Windows (for any other build mode), the garbage collector finds static variables in an embedding program by examining all memory pages. This strategy fails if a program contains multiple Windows threads; a page may get unmapped by a thread while the collector is examining the page, causing the collector to crash. To avoid this problem, call scheme_main_setup with a non-zero first argument.

When an embedding application calls scheme_main_setup with a non-zero first argument, it must register each of its static variables with MZ_REGISTER_STATIC if the variable can contain a GCable pointer. For example, if curout above is made static, then MZ_REGISTER_STATIC(curout) should be inserted before the call to scheme_get_param.

When building an embedded Racket CGC to use SenoraGC (SGC) instead of the default collector, scheme_main_setup must be called with a non-zero first argument. See Memory Allocation for more information.

1.4.2 3m Embedding

Racket 3m can be embedded mostly the same as Racket, as long as the embedding program cooperates with the precise garbage collector as described in Cooperating with 3m.

In either your source in the in compiler command line, #define MZ_PRECISE_GC before including "scheme.h". When using raco ctool with the --cc and --3m flags, MZ_PRECISE_GC is automatically defined.

In addition, some library details are different:

For Racket 3m, an embedding application must call scheme_main_setup with a non-zero first argument.

The simple embedding program from the previous section can be processed by raco ctool --xform, then compiled and linked with Racket 3m. Alternately, the source code can be extended to work with either CGC or 3m depending on whether MZ_PRECISE_GC is defined on the compiler’s command line:

  #include "scheme.h"

  

  #include "base.c"

  

  static int run(Scheme_Env *e, int argc, char *argv[])

  {

    Scheme_Object *curout = NULL, *v = NULL, *a[2] = {NULL, NULL};

    Scheme_Config *config = NULL;

    int i;

    mz_jmp_buf * volatile save = NULL, fresh;

  

    MZ_GC_DECL_REG(8);

    MZ_GC_VAR_IN_REG(0, e);

    MZ_GC_VAR_IN_REG(1, curout);

    MZ_GC_VAR_IN_REG(2, save);

    MZ_GC_VAR_IN_REG(3, config);

    MZ_GC_VAR_IN_REG(4, v);

    MZ_GC_ARRAY_VAR_IN_REG(5, a, 2);

  

    MZ_GC_REG();

  

    declare_modules(e);

  

    v = scheme_intern_symbol("racket/base");

    scheme_namespace_require(v);

  

    config = scheme_current_config();

    curout = scheme_get_param(config, MZCONFIG_OUTPUT_PORT);

  

    for (i = 1; i < argc; i++) {

      save = scheme_current_thread->error_buf;

      scheme_current_thread->error_buf = &fresh;

      if (scheme_setjmp(scheme_error_buf)) {

        scheme_current_thread->error_buf = save;

        return -1; /* There was an error */

      } else {

        v = scheme_eval_string(argv[i], e);

        scheme_display(v, curout);

        v = scheme_make_char('\n');

        scheme_display(v, curout);

        /* read-eval-print loop, uses initial Scheme_Env: */

        a[0] = scheme_intern_symbol("racket/base");

        a[1] = scheme_intern_symbol("read-eval-print-loop");

        v = scheme_dynamic_require(2, a);

        scheme_apply(v, 0, NULL);

        scheme_current_thread->error_buf = save;

      }

    }

  

    MZ_GC_UNREG();

  

    return 0;

  }

  

  int main(int argc, char *argv[])

  {

    return scheme_main_setup(1, run, argc, argv);

  }

Strictly speaking, the config and v variables above need not be registered with the garbage collector, since their values are not needed across function calls that allocate. The code is much easier to maintain, however, when all local variables are registered and when all temporary values are put into variables.

1.5 Racket and Threads

Racket implements threads for Racket programs without aid from the operating system, so that Racket threads are cooperative from the perspective of C code. Under Unix, stand-alone Racket uses a single OS-implemented thread. Under Windows and Mac OS X, stand-alone Racket uses a few private OS-implemented threads for background tasks, but these OS-implemented threads are never exposed by the Racket API.

In an embedding application, Racket can co-exist with additional OS-implemented threads, but the additional OS threads must not call any scheme_ function. Only the OS thread that originally calls scheme_basic_env can call scheme_ functions. (This restriction is stronger than saying all calls must be serialized across threads. Racket relies on properties of specific threads to avoid stack overflow and garbage collection.) When scheme_basic_env is called a second time to reset the interpreter, it can be called in an OS thread that is different from the original call to scheme_basic_env. Thereafter, all calls to scheme_ functions must originate from the new thread.

See Threads for more information about threads, including the possible effects of Racket’s thread implementation on extension and embedding C code.

1.6 Racket, Unicode, Characters, and Strings

A character in Racket is a Unicode code point. In C, a character value has type mzchar, which is an alias for unsigned which is, in turn, 4 bytes for a properly compiled Racket. Thus, a mzchar* string is effectively a UCS-4 string.

Only a few Racket functions use mzchar*. Instead, most functions accept char* strings. When such byte strings are to be used as a character strings, they are interpreted as UTF-8 encodings. A plain ASCII string is always acceptable in such cases, since the UTF-8 encoding of an ASCII string is itself.

See also Strings and String Encodings.

1.7 Integers

Racket expects to be compiled in a mode where short is a 16-bit integer, int is a 32-bit integer, and intptr_t has the same number of bits as void*. The long type can match either int or intptr_t, depending on the platform. The mzlonglong type has 64 bits for compilers that support a 64-bit integer type, otherwise it is the same as intptr_t; thus, mzlonglong tends to match long long. The umzlonglong type is the unsigned version of mzlonglong.