| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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.
4.1 Two world views 4.2 What is libguile 4.3 How to get started with libguile 4.4 More interesting programming with libguile 4.5 Further examples
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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, I 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 large program designer, you have to decide which of those capabilities to use. You have two main choices:
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. When we go
through examples of libguile use, we will point out which is the master
world in order to clarify these two approaches.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Libguile is the library which allows 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.
The interface provided by the libguile C library is somewhat specific to
the implementation of the Scheme interpreter. This low-level libguile
interface is usually referred to as the scm_ interface, since its
public calls (API) all have the scm_ prefix.
There is also a higher-level libguile interface, which is usually
referred to as the gh_ interface (libGuile High). Its public
calls all have the gh_ prefix. The gh_ library interface
is designed to hide the implementation details, thus making it easier to
assimilate and portable to other underlying Scheme implementations.
People extending Guile by adding bindings to C libraries (like OpenGL or
Rx) are encouraged to use the gh_ interface, so their work will
be portable to other Scheme systems. The gh_ interface should be
more stable, because it is simpler.
The scm_ interface is necessary if you want to poke into the
innards of Scheme data structures, or do anything else that is not
offered by the gh_ interface. It is not covered in this
tutorial, but is covered extensively in @xref{Scheme data representation, Guile Reference Manual, guile-ref, Guile Reference Manual}.
This chapter gives a gentle introduction to the gh_ interface,
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 at http://nis-www.lanl.gov/~rosalia/mydocs/ to see a large scale project that uses C and Scheme code together.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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:
/* test the new libgh.a (Guile High-level library) with a trivial
program */
#include <stdio.h>
#include <guile/gh.h>
void main_prog(int argc, char *argv[]);
main(int argc, char *argv[])
{
gh_enter(argc, argv, main_prog);
}
void main_prog(int argc, char *argv[])
{
int done;
char input_str[200];
gh_eval_str("(display \"hello Guile\")");
gh_eval_str("(newline)");
/* for fun, evaluate some simple Scheme expressions here */
gh_eval_str("(define (square x) (* x x))");
gh_eval_str("(define (fact n) (if (= n 1) 1 (* n (fact (- n 1)))))");
gh_eval_str("(square 9)");
/* now sit in a Scheme eval loop: I input the expressions, have
Guile evaluate them, and then get another expression. */
done = 0;
fputs("learn0> ", stdout);
while (fgets(input_str, 199, stdin) != NULL) {
gh_eval_str(input_str);
fputs("\nlearn0> ", stdout);
}
exit(0);
}
|
If you name this program learn0.c, it can now be compiled with:
gcc -g -c learn0.c -o learn0.o gcc -o learn0 learn0.o -lguile -lm |
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-print-loop (REPL), so you could type one-line Scheme expressions to it and have them evaluated. For example:
<shell-prompt> ./learn0 hello Guile learn0> (display (sin 1.3)) 963.558185417193e-3 learn0> (display (fact 10)) 3628800 learn0> (quit) <shell-prompt> |
You should notice the key steps involved in this learn0 program:
#include <guile/gh.h>
gh_enter(). This
starts up a Scheme interpreter, handling many implementation-specific
details.
gh_enter().
gh_eval_str()
routine.
-lguile.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 primitive 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.
Notice that learn1 uses a Scheme master world, and the C routines
in c_builtins.c are simply adding new primitives to Scheme.
4.4.1 learn1.c 4.4.2 c_builtins.h 4.4.3 c_builtins.c 4.4.4 What learn1 is doing 4.4.5 Compiling and running learn1
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Here is `learn1.c':
#include <stdio.h>
#include <guile/gh.h>
#include "c_builtins.h"
void main_prog(int argc, char *argv[]);
main(int argc, char *argv[])
{
gh_enter(argc, argv, main_prog);
}
void main_prog(int argc, char *argv[])
{
char input_str[200]; /* ugly hack: assume strlen(line) < 200 */
int done;
/* for fun, evaluate some simple Scheme expressions here */
gh_eval_str("(define (square x) (* x x))");
gh_eval_str("(define (fact n) (if (= n 1) 1 (* n (fact (- n 1)))))");
gh_eval_str("(square 9)");
gh_eval_str("(fact 100)");
/* now try to define some new builtins, coded in C, so that they are
available in Scheme. */
gh_new_procedure1_0("c-factorial", c_factorial);
gh_new_procedure1_0("c-sin", c_sin);
gh_new_procedure1_0("v-t", vector_test);
/* 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) {
done = 1;
} else {
gh_eval_str(input_str);
fputs("learn1> ", stdout);
}
}
exit(0);
}
|
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Here is `c_builtins.h':
/* builtin function prototypes */ #include <guile/gh.h> SCM c_factorial(SCM n); SCM c_sin(SCM n); SCM vector_test(SCM s_length); |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Here is `c_builtins.c':
#include <stdio.h>
#include <math.h>
#include <guile/gh.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 = gh_scm2ulong(s_n);
gh_defer_ints();
for (i = 1; i <= n; ++i) {
result = result*i;
}
gh_allow_ints();
return gh_ulong2scm(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 = gh_scm2double(s_x);
return gh_double2scm(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;
c_length = gh_scm2ulong(s_length);
printf("requested length for vector: %ld\n", gh_scm2ulong(s_length));
/* create a vector */
xvec = gh_make_vector(s_length, gh_double2scm(0.0));
/* set the second element in it */
gh_vector_set_x(xvec, gh_int2scm(2), gh_double2scm(1.9));
return xvec;
}
|
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If you compare learn1 to learn0, you will find that learn1 uses a new
Guile construct: the function gh_new_procedure(), and its
siblings:
/* now try to define some new builtins, coded in C, so that they are
available in Scheme. */
gh_new_procedure1_0("c-factorial", c_factorial);
gh_new_procedure1_0("c-sin", c_sin);
gh_new_procedure1_0("v-t", vector_test);
|
It is clear that gh_new_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 <guile/gh.h>
gh_enter(). This
starts up a Scheme interpreter, handling many details.
gh_enter().
gh_eval_str()
routine.
gh_new_procedure() routine.
gh_eval_str() routine.
-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.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gcc -g -c learn1.c -o learn1.o gcc -g -c c_builtins.c -o c_builtins.o gcc -o learn1 learn1.o c_builtins.o -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 welcome to Guile hello Guile learn1> (display (c-factorial 6)) 720 learn1> (display (c-factorial 20)) 2192834560 learn1> (display (c-factorial 100)) 0 learn1> (display (c-sin 1.5)) 0.997494986604054 learn1> (display (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> (display (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.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Further "idealized" examples are included in the doc/examples/c
distribution. They include programs to:
| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |