Go to the previous, next section.
The learn0 program shows how you can invoke scheme commands from
a C program. That 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 from 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 source code and the
Makefile that come in the learn_libguile distribution.
Here is `learn1.c':
#include <stdio.h>
#include <gscm.h>
/*
#include <tcl.h>
#include <tk.h>
*/
#include "c_builtins.h"
static GSCM_status guile_init()
{
#if 0 /* don't need fancy packages */
scm_init_ctax();
scm_init_unix();
scm_init_posix();
scm_init_ioext();
scm_init_gtcl();
scm_init_gtk();
#endif /* 0 */
}
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 */
#if 0 /* this is the old way */
status = initialize_gscm(0, argc, argv);
#endif /* 0 */
/* 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);
}
/* now some code to prepare a Tk main window; the scheme code would be
* something like
* (define tk-main (tk-init-main-window (tcl-create-interp) "papageno:0"
* "john" "john2")`w)
* where I am still confused about the "name" and "class" parameters
* involved.
*/
/* ...abandoned for a while; can't figure it out */
/* 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);
GSCM_DEFER_INTS;
for (i = 1; i <= n; ++i) {
result = result*i;
}
GSCM_ALLOW_INTS;
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 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");
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 rscm_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. 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.
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 SLIB "2a2" on GUILE iii on UNIX edit GUILE ".init" to set (scheme-implementation-version) (IMPLEMENTATION-VICINITY) is "/packages/lib/gls/" type (slib:report) for configuration 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.49498660405e-3 learn1> (print (sin 1.5)) 997.49498660405e-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) ERROR: unbound variable: %gscm-indirect <153 sst10b->proto1>
Go to the previous, next section.