Go to the first, previous, next, last section, table of contents.

Guile in a library - using libguile.a

In the previous chapters Guile was used to write programs entirely in scheme, and no C code was seen; but I have been claiming ad nauseam that Guile is an extension language. Here we see how that is done, and how that can be useful.

Two world views

In this manual I usually jump into examples and explain them as you type in the code; here I will digress and ramble for a few paragraphs to set some concepts straight, and then let you type (or paste) in fun examples.

In 1995 the author of this manual implemented a large program, Gnudl, using Guile quite extensively. In the design phase of Gnudl I found I had to make a choice: should the fundamental data structures be C or scheme data structures?

Guile allows C to see its data structures (scalar types, lists, vectors, strings ...). C also allows guile to see its data structures. As a largre program designer, you have to decide which of those capabilities to use. You have two main choices.

You can write your software mostly in scheme. In this case, your C software will mostly parse the scheme code with Guile calls, and provide some new primitive procedures to be used by scheme. This is what Gnudl does.

Or you can write your software mostly in C, occasionally allowing scheme code to be parsed by Guile, either to allow the user to modify data structures, or to parse a configuration file, ...

Mixing the two approaches seems unwise: the overall layout would be confusing. But who knows? There might be problems that are best solved by a hybrid approach. Please let me know if you think of such a problem.

If you use the former approach, we will say that the master world is scheme, and the C routines serve scheme and access scheme data structures. In the latter case, the master world is C, and scheme routines serve the C code and access C data structures.

In both approaches the libguile.a library is the same, but a predominantly different set of routines will be used. To clarify these two approaches further, when we go through examples of libguile use, we will point out which is the master world.

What is libguile

Libguile is the library which can be linked to C programs and which allows the C programs to start a scheme interpreter and execute scheme code. There are also facilities in libguile to make C data structures available to scheme and vice versa.

This chapter gives a gentle introduction to libguile.a, presenting some hello world-style programs which I wrote while teaching myself to use libguile.

The Guile Programmer's Manual gives more examples of programs written using libguile, illustrating diverse applications. You can also consult my Gnudl documentation to see a large scale project that uses C and scheme code together.

How to get started with libguile

Here is an elementary first program, learn0, to get going with libguile. The program (which uses scheme as a master world) is in a single source file learn0.c:

#include <stdio.h>

/* the include file for guile */
#include <gscm.h>

static GSCM_status guile_init()
{
  /* don't need any extra packages */
}

main(int argc, char *argv[])
{
  GSCM_status status;
  GSCM_top_level toplev;
  char *eval_answer;
  char input_str[200];
  int done;

  printf("hello guile\n");

  /* start a scheme interpreter */
  status = gscm_run_scm(argc, argv, 0, stdout, stderr, guile_init, 0, "#t");
  if (status != GSCM_OK) {
    fputs(gscm_error_msg(status), stderr);
    fputc('\n', stderr);
    exit(1);
  }

  /* create the top level environment */
  status = gscm_create_top_level(&toplev);
  if (status != GSCM_OK) {
    fputs(gscm_error_msg(status), stderr);
    fputc('\n', stderr);
    exit(1);
  }

  /* for fun, evaluate some simple scheme expressions here */
  status = gscm_eval_str(NULL, toplev, "(define (square x) (* x x))");
  status = gscm_eval_str(NULL, toplev,
            "(define (factorial n) (if (= n 1) 1 (* n (factorial (- n 1)))))");
  status = gscm_eval_str(NULL, toplev, "(square 9)");
  status = gscm_eval_str(NULL, toplev, "(factorial 100)");

  /* now sit in a scheme eval loop: I input the expressions, have guile
   * evaluate them, and then get another expression.
   */
  done = 0;
  while (!done) {
    if (gets(input_str) == NULL || strcmp(input_str, "quit") == 0) {
      done = 1;
    } else {
      status = gscm_eval_str(NULL, toplev, input_str);
    }
  }

  /* now clean up and quit */
  gscm_destroy_top_level(toplev);
  exit(0);
}

If you name this program learn0.c, it can now be compiled with:

gcc -g -I/packages/include/guile -c learn0.c -o learn0.o
gcc -o learn0 learn0.o -L/packages/lib -lguile -lm

(remember to fix the paths if you don't use `/packages' as a prefix).

The program is simple: it creates a scheme interpreter, passes a couple of strings to it that define new scheme functions square and factorial, and then a couple of strings that invoke those functions.

It then goes into a read-eval loop, so you could type one-line scheme expressions to it and have them evaluated. For example:

<shell-prompt> ./learn0
hello guile
;;; loading modops...
;;; ...loaded /packages/lib/gls/bootstrapping/modops.
;;; loading libguile...
;;; ...loaded /packages/lib/gls/bootstrapping/libguile.
;;; loading slibhooks...
;;; ...loaded /packages/lib/gls/bootstrapping/slibhooks.
;;; loading require...
;;;   loading slibcat...
;;;   ...loaded /packages/lib/gls/bootstrapping/slibcat.
;;; ...loaded /packages/lib/gls/bootstrapping/require.
;;; loading mule...
;;; ...loaded /packages/lib/gls/bootstrapping/mule.
learn0> (require 'debug)
;;; loading debug...
;;; ...loaded /packages/lib/gls/debug.
; type (init-debug)learn0> 
learn0> (print (sin 1.3))
963.558185417193e-3 
learn0> (print (factorial 10))
3628800 
learn0> (quit)
<shell-prompt> 

You should notice the key steps involved in this learn0 program:

  1. #include <gscm.h>
  2. You need a guile_init()-like routine, which could even be trivial like the one in this example, to pass to gscm_run_scm(). This allows you to tell gscm_run_scm() which optional guile packages to use. Note that in this example I am only interested in the scheme interpreter, and not in the extra packages, so all the extra package initialization has been #if-ed out.
  3. You must invoke gscm_run_scm() and gscm_create_top_level() at the beginning of your program. This sets up the scheme interpreter to which you can then pass strings for evaluation.
  4. You pass strings to the scheme interpreter with the gscm_eval_str() routine.
  5. You quit with gscm_destroy_top_level().
  6. You link your program with -lguile.

More interesting programming with libguile

The learn0 program shows how you can invoke scheme commands from a C program. This is not such a great achievement: the same could have been done by opening a pipe to SCM or any other scheme interpreter.

A true extension language must allow callbacks. Callbacks allow you to write C routines that can be invoked as scheme procedures, thus adding new builting procedures to scheme. This also means that a scheme procedure can modify a C data structure.

Guile allows you to define new scheme procedures in C, and provides a mechanism to go back and forth between C and scheme data types.

Here is a second program, learn1, which demonstrates these features. It is split into three source files: learn1.c, c_builtins.h and c_builtins.c. I am including the code here, but you might just want to look at the online source code and the Makefile that come with Guile in the `examples' directory. Notice that learn1 uses a scheme master world, and the C routines in c_builtins.c are simply adding new primitives to scheme.

learn1.c

Here is `learn1.c':

#include <stdio.h>

#include <gscm.h>

#include "c_builtins.h"

static GSCM_status guile_init()
{
  /* don't need fancy packages */
}

main(int argc, char *argv[])
{
  GSCM_status status;
  GSCM_top_level toplev;
  char *eval_answer;
  char input_str[200];          /* ugly hack: assume strlen(line) < 200 */
  int done;

  printf("hello guile\n");

  /* start an interpreter */
  /* start a scheme interpreter */
  status = gscm_run_scm(argc, argv, 0, stdout, stderr, guile_init, 0, "#t");
  if (status != GSCM_OK) {
    fputs(gscm_error_msg(status), stderr);
    fputc('\n', stderr);
    exit(1);
  }

  /* create the top level environment */
  status = gscm_create_top_level(&toplev);
  if (status != GSCM_OK) {
    fputs(gscm_error_msg(status), stderr);
    fputc('\n', stderr);
    exit(1);
  }

  /* for fun, evaluate some simple scheme expressions here */
  status = gscm_eval_str(NULL, toplev, "(define (square x) (* x x))");
  status = gscm_eval_str(NULL, toplev,
            "(define (factorial n) (if (= n 1) 1 (* n (factorial (- n 1)))))");
  status = gscm_eval_str(NULL, toplev, "(square 9)");
  status = gscm_eval_str(NULL, toplev, "(factorial 100)");

  /* now try to define some new builtins, coded in C, so that they
   * are available in scheme.  note that I have put junk in the documentation
   * strings because I have not yet read the manual on how doc strings
   * should be written :-|
   */
  gscm_define_procedure("c-factorial", c_factorial, 1, 1, 0, "hi there");
  gscm_define_procedure("c-sin", c_sin, 1, 1, 0, "hi there");
  gscm_define_procedure("v-t", vector_test, 1, 1, 0, "hi there");

  /* now sit in a scheme eval loop: I input the expressions, have guile
   * evaluate them, and then get another expression.
   */
  done = 0;
  fputs("learn1> ", stdout);
  while (!done) {
    if (gets(input_str) == NULL || strcmp(input_str, "(quit)") == 0) {
      done = 1;
    } else {
      status = gscm_eval_str(NULL, toplev, input_str);
      fputs("learn1> ", stdout);
    }
  }

  /* now clean up and quit */
  gscm_destroy_top_level(toplev);
  exit(0);
}

c_builtins.h

Here is `c_builtins.h':

/* builtin function prototypes */

SCM c_factorial(SCM n);
SCM c_sin(SCM n);
SCM vector_test(SCM s_length);

c_builtins.c

Here is `c_builtins.c':

#include <stdio.h>
#include <math.h>

#include <gscm.h>

#include "c_builtins.h"

/* this is a factorial routine in C, made to be callable by scheme */
SCM c_factorial(SCM s_n)
{
  int i;
  unsigned long result = 1, n;

  n = gscm_2_ulong(s_n);

  for (i = 1; i <= n; ++i) {
    result = result*i;
  }
  return gscm_ulong(result);
}

/* a sin routine in C, callable from scheme.  it is named c_sin()
 * to distinguish it from the default scheme sin function
 */
SCM c_sin(SCM s_x)
{
  double x = gscm_2_double(s_x);

  return gscm_double(sin(x));
}

/* play around with vectors in guile: this routine creates a
 * vector of the given length, initializes it all to zero except
 * element 2 which is set to 1.9.
 */
SCM vector_test(SCM s_length)
{
  SCM xvec;
  unsigned long c_length;

  c_length = gscm_2_ulong(s_length);
  printf("requested length for vector: %ld\n", c_length);

  /* create a vector */
  xvec = gscm_vector(c_length, gscm_double(0.0));
  /* set the second element in it */
  gscm_vset(xvec, 2, gscm_double(1.9));

  return xvec;
}

What learn1 is doing

If you compare learn1 to learn0, you will find that learn1 uses a new guile construct: the function gscm_define_procedure():

  /* now try to define some new builtins, coded in C, so that they
   * are available in scheme.  note that I have put junk in the
   * documentation strings because they are not yet stable.
   */
  gscm_define_procedure("c-factorial", c_factorial, 1, 1, 0, "doc str");
  gscm_define_procedure("c-sin", c_sin, 1, 1, 0, "doc str");
  gscm_define_procedure("v-t", vector_test, 1, 1, 0, "doc str");

It is clear that gscm_define_procedure() adds a new builtin routine written in C which can be invoked from scheme. We can now revise our checklist for programming with libguile, so it includes adding callbacks.

  1. #include <gscm.h>
  2. You need a guile_init()-like routine, which could even be trivial like the one in this example, to pass to gscm_run_scm(). This allows you to tell gscm_run_scm() which optional guile packages to use. Note that in this example I am only interested in the scheme interpreter, and not in the extra packages, so there are no calls to init_tcl(), init_tk() and so forth.
  3. You must invoke gscm_run_scm() and gscm_create_top_level() at the beginning of your program. This sets up the scheme interpreter to which you can then pass strings for evaluation. I believe that this routine will soon be renamed to start with the prefix gscm_: the prefix of all the routines in the guile programmer's interface.
  4. [new] You can now register callbacks; i.e. define new builtin scheme functions, with the gscm_define_procedure() routine.
  5. You pass strings to the scheme interpreter with the gscm_eval_str() routine.
  6. You quit with gscm_destroy_top_level() (which currently gives some silly error message).
  7. You link your program with -lguile.

I breezed by the issue of how to write your C routines that are registered to be called from scheme. This is non-trivial, and is discussed at length in the Guile Programmer's Manual.

Compiling and running learn1

gcc -g -I/packages/include/guile   -c learn1.c -o learn1.o
gcc -g -I/packages/include/guile   -c c_builtins.c -o c_builtins.o
gcc -o learn1 learn1.o c_builtins.o -L/packages/lib/scm -L/packages/lib -lguile -lm

If you run learn1, it will prompt you for a one-line scheme expression, just as learn0 did. The difference is that you can use the new C builtin procedures (c-factorial, c-sin, v-t).

<shell-prompt> ./learn1
hello guile
;;; loading modops...
;;; ...loaded /packages/lib/gls/bootstrapping/modops.
;;; loading libguile...
;;; ...loaded /packages/lib/gls/bootstrapping/libguile.
;;; loading slibhooks...
;;; ...loaded /packages/lib/gls/bootstrapping/slibhooks.
;;; loading require...
;;;   loading slibcat...
;;;   ...loaded /packages/lib/gls/bootstrapping/slibcat.
;;; ...loaded /packages/lib/gls/bootstrapping/require.
;;; loading mule...
;;; ...loaded /packages/lib/gls/bootstrapping/mule.
learn1> (require 'debug)
;;; loading debug...
;;; ...loaded /packages/lib/gls/debug.
; type (init-debug)learn1> 
learn1> (print (c-factorial 6))
720 
learn1> (print (c-factorial 20))
2192834560 
learn1> (print (c-factorial 100))
0 
learn1> (print (c-sin 1.5))
997.494986604054e-3 
learn1> (print (c-sin 1.5))
997.494986604054e-3 
learn1> (print (v-t 10))
requested length for vector: 10
#(0.0 0.0 1.9 0.0 0.0 0.0 0.0 0.0 0.0 0.0) 
learn1> (print (v-t 15))
requested length for vector: 15
#(0.0 0.0 1.9 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0) 
learn1> (quit)
<shell-prompt> 

As you see, taking (c-factorial 100) does not use bignumbers and returns a bogus answer.

Further examples

Further "idealized" examples are included in the examples/c distribution. They include programs to:


Go to the first, previous, next, last section, table of contents.