6.3  Entry points

To simplify embedding compiled Scheme code into arbitrary programs, one can define so called ``entry points'', which provide a uniform interface and parameter conversion facilities.

[syntax] (define-entry-point INDEX ((VAR1 TYPE1) ...) (RTYPE1 ...) EXP1 EXP2 ...)
Defines a new entry-point with index INDEX which should evaluate to an exact integer. During execution of the body EXP1 EXP2 ... the variables VAR1 ... are bound to the parameters passed from the host program to the invoked entry point. The parameters passed are converted according to the foreign type specifiers TYPE1 .... The expressions should return as many values as foreign type specifiers are given in RTYPE1 .... The results are then transformed into values that can be used in the host program.

Note: if one or more of the result types RTYPE ... specify the type c-string, then the parameter types at the same positions in TYPE1 ... have to be c-strings as well, because the result strings are copied into the same area in memory. You should also take care that the passed buffer is long enough to hold the result string or unpredictable things will happen.

If entry points were defined then the program will not terminate after execution of the last toplevel expression, but instead it will enter a loop that waits for the host to invoke one of the defined entry points.

The following C functions and data types are provided:

[C function] void CHICKEN_parse_command_line(int argc, char *argv[], int *heap, int *stack int *symbols)
Parse the programs command-line contained in argc and argv and return the heap-, stack- and symbol table limits given by runtime options of the form -:..., or choose default limits. The library procedure argv can access the command-line only if this function has been called by the containing application.

[C function] int CHICKEN_initialize(int heap, int stack, int symbols, void *toplevel)
Initializes the Scheme execution context and memory. heap holds the number of bytes that are to be allocated for the secondary heap. stack holds the number of bytes for the primary heap. symbols contains the size of the symbol table. Passing 0 to one or more of these parameters will select a default size. toplevel should be a pointer to the toplevel entry point procedure. You should pass C_toplevel here. In any subsequent call to CHICKEN_run or CHICKEN_invoke you can simply pass NULL. Calling this function more than once has no effect. If enough memory is available and initialization was successful, then 1 is returned, otherwise this function returns 0.

[C function] void CHICKEN_run(void **data, int *bytes, int *maxlen, void *toplevel)
Starts the Scheme program. data, bytes and maxlen contain invocation parameters in raw form. Pass NULL here. Call this function once to execute all toplevel expressions in your compiled Scheme program. If the runtime system was not initialized before, then CHICKEN_initialize is called with default sizes. toplevel is the toplevel entry-point procedure.

[C function] void CHICKEN_invoke(int index, C_parameter *params, int count, void *toplevel)
Invoke the entry point with index index. count should contain the number of parameters passed. params is a pointer to parameter data:

typedef union
{
  C_word x;           /* parameter type scheme-object */
  long i;             /* parameter type bool, [unsigned] int/short/long */
  long c;             /* parameter type [unsigned] char */
  double f;           /* parameter type float/double */
  void *p;            /* any pointer parameter type and C strings */
} C_parameter;

This function calls CHICKEN_run if it was not called at least once before.

Here is a simple example (assuming a UNIX-like environment):

% cat foo.c
#include <stdio.h>
#include "chicken.h"

int main(void)
{
  C_parameter p[ 3 ];
  char str[ 32 ] = "hello!";  /* We need some space for the result string! */

  memset(p, 0, sizeof(p));
  p[ 0 ].i = -99;
  p[ 1 ].p = str;
  p[ 2 ].f = 3.14;
  CHICKEN_invoke(1, p, 3, C_toplevel);
  printf("->\n%d\n%s\n", p[ 0 ].i, p[ 1 ].p);
  return 0;
}

% cat bar.scm
(define-entry-point 1
    ((a integer) (b c-string) (c double))
    (int c-string)
  (print (list a b c))
  (values 123 "good bye!") )

% chicken bar.scm -quiet
% gcc foo.c bar.c -o foo `chicken-config -cflags -embedded`
% foo
(-99 "hello!" 3.14)
->
123
good bye!

Note the use of -embedded. We have to compile with additional compiler options, because the host program provides the main function.

Chicken also provides ``boilerplate'' entry points, that simplify invoking Scheme code embedded in a C or C++ application tremendously. The include file default-entry-points.scm will define entry-points for common usage patterns, like loading a file, evaluating an expression or calling a procedure.

[C macro] CHICKEN_eval(C_word exp, C_word *result, int *status)
Evaluates the Scheme object passed in exp, writing the result value to result. If the evaluation triggered an error, status is set to 1 if the operation succeeded, or 0 if an error occurred. Call CHICKEN_get_error_message to obtain a description of the error.

[C macro] CHICKEN_eval_string(char *str, C_word *result, int *status)
Evaluates the Scheme expression passed in the string str, writing the result value to result.

[C macro] CHICKEN_eval_to_string(C_word exp, char *result, int size, int *status)
Evaluates the Scheme expression passed in exp, writing a textual representation of the result into result. size should specify the maximal size of the result string.

[C macro] CHICKEN_eval_string_to_string(char *str, char *result, int size, int *status)
Evaluates the Scheme expression passed in the string str, writing a textual representation of the result into result. size should specify the maximal size of the result string.

[C macro] CHICKEN_apply(C_word func, C_word args, C_word *result, int *status)
Applies the procedure passed in func to the list of arguments args, writing the result value to result.

[C macro] CHICKEN_apply_to_string(C_word func, C_word args, char *result, int size, int *status)
Applies the procedure passed in func to the list of arguments args, writing a textual representation of the result into result.

[C macro] CHICKEN_read(char *str, C_word *result, int *status)
Reads a Scheme object from the string str, writing the result value to result.

[C macro] CHICKEN_load(char *filename, int *status)
Loads the Scheme file filename (either in source form or compiled).

[C macro] CHICKEN_get_error_message(char *result, int size)
Returns a textual description, in case an error occurred while invoking embedded Scheme code.

[C macro] CHICKEN_yield(int *status)
If threads have been spawned during earlier invocations of embedded Scheme code, then this function will run the next scheduled thread for one complete time-slice. This is useful, for example, inside an ``idle'' handler in a GUI application with background Scheme threads.

An example:

% cat x.scm
;;; x.scm

(include "default-entry-points")
(define (bar x) (gc) (* x x))

% cat y.c
/* y.c */

#include "chicken.h"
#include <assert.h>

int main() {
  char buffer[ 256 ];
  int status;
  C_word val = C_SCHEME_UNDEFINED;
  C_word *data[ 1 ];
  
  data[ 0 ] = &val;

  CHICKEN_read("(bar 99)", &val, &status);
  assert(status);

  C_gc_protect(data, 1);

  printf("data: %08x\n", val);

  CHICKEN_eval_string_to_string("(bar)", buffer, 255, &status);
  assert(!status);

  CHICKEN_get_error_message(buffer, 255);
  printf("ouch: %s\n", buffer);

  CHICKEN_eval_string_to_string("(bar 23)", buffer, 255, &status);
  assert(status);

  printf("-> %s\n", buffer);
  printf("data: %08x\n", val);

  CHICKEN_eval_to_string(val, buffer, 255, &status);
  assert(status);
  printf("-> %s\n", buffer);

  return 0;
}

% csc x.scm y.c -embedded