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.
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.
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.
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:
#include <gscm.h>
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.
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.
gscm_eval_str() routine.
gscm_destroy_top_level().
-lguile.
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.
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);
}
Here is `c_builtins.h':
/* builtin function prototypes */ SCM c_factorial(SCM n); SCM c_sin(SCM n); SCM vector_test(SCM s_length);
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;
}
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.
#include <gscm.h>
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.
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.
gscm_define_procedure() routine.
gscm_eval_str() routine.
gscm_destroy_top_level() (which currently gives
some silly error message).
-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.
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 "idealized" examples are included in the examples/c
distribution. They include programs to: