Table of Contents


Node:Top, Next:, Up:(dir)

Introduction

ID: $Id: OLD-guile-faq.html,v 1.1 2000/07/25 19:17:37 ttn Exp $

[NOTE! This FAQ is being overhauled. Please send comments about it directly to ttn@gnu.org.]

Guile is an implementation of the Scheme programming language built to be used as an extension language. The interesting thing about this Scheme implementation is that it is the official extension language of the GNU project, which means that it is likely that guile will be used in an increasing number of GNU programs.

This document aims to help the programmer who is trying to get started with guile. It is not necessary to know a lot about Scheme or guile in particular, however it is assumed that the reader knows a bit about writing and building C programs. Note that this document does not pretend to be a definitive reference document for guile; use the guile reference manual for that. Instead it is supposed to be a pointer to more detailed information about guile, a cookbook of common idioms for using guile, and a list of common problems with guile and their resolution.

The discussion focuses around the 1.3 [fixme] release of guile. The examples in this document should all compile and run correctly in this version of guile. You may use the example programs in any way you wish (see Copying).


Node:Copying, Next:, Previous:Top, Up:Top

Copying

Copyright (C) 2000 Free Software Foundation

This document is free documentation; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

As a special exception, the programs contained in this document are in the public domain. There are no restrictions on how you may use these programs. This exception applies only to the machine executable programs contained in the body of the document.

This document and any included programs in the document are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.


Node:More info, Next:, Previous:Copying, Up:Top

More info

This document is far from an exhaustive listing of information about guile. This is not an exhaustive list of guile links, but instead an attempt to provide a number of useful starting points for the interested programmer to find more information.


http://www.red-bean.com/guile
The unofficial guile home page. One can find some introductory material about guile here, along with lots of pointers to more information.
http://www.red-bean.com/guile/guile/old/index.html
The "old" guile mailing list archive, which goes up to August 1998.
http://www.red-bean.com/guile/guile/new/threads.html
The "new" guile mailing list, which has messages up to the present (hopefully).
http://home.thezone.net/~gharvey/guile/guile-projects.html
Greg Harvey's temporary guile projects page. This page has a list of all known publicly available guile related projects, applications, tools, add-ons, you name it.
http://home.thezone.net/~gharvey/guile/qdocs/index.html
Greg Harvey's documentation of guile internals. These are incomplete, but very useful if you want to know how guile works.
ftp://ftp.red-bean.com/pub/guile
The guile developer's ftp site. You can pick up some contributed software as well as guile development snapshots here.


Node:Building, Next:, Previous:More info, Up:Top

Building

One of the uses for guile is as an extension language to C and C++ programs. In order to do this, one needs to know how to link guile into your program (exhaustive documentation of this is included in the guile reference manual), write new primitive procedures in C, convert data between Scheme and C, and manage the control flow between Scheme and C. This information is covered exhaustively in the guile reference manual, however there are some common gotcha's.


Node:Snarfing, Next:, Up:Building

Snarfing

Snarfing is the process of automatically grabbing primitive procedure definitions from C source, and registering those procedures with the run time. This is helpful because defining a primitive from C is a two step process; first the C function that implements the primitive must be written, and then this C function must be registered by name with the Scheme runtime. Without guile-snarf, this registration would happen at a place in the source physically distant from the place where the C function is itself defined, which makes adding or changing primitive definitions awkward and error prone.


Node:What is guile-snarf?, Next:, Up:Snarfing

What is guile-snarf?

guile-snarf is a utility program that gets installed into the same directory as guile itself when you build and install the guile package. It is used to make the maintenance of C source files simpler, in the specific case when the user is defining new guile primitives (there are some other uses for the program, but for now let's stick to this).

Please note that using guile-snarf is completely optional; the guile sources themselves use it, but that doesn't mean that you have to do so. Many people seem to think that it's simpler to use the gh_ functions (FIXME: add more stuff about gh functions for adding primitives).


Node:SCM_PROC, Next:, Previous:What is guile-snarf?, Up:Snarfing

SCM_PROC

guile-snarf uses some C pre-processor trickery to allow the primitive registration to remain physically close to the primitive definition. The C preprocessor marches through C source files looking for macro invocations of SCM_PROC, each of which expands into code that registers the new primitive procedure. The expanded code is stored away into another file, conventionally a file with the same name as the source file, with a .x extension. Then the file containing the expanded code is re-introduced into the source file at the appropriate place using a C preprocessor #include directive. An example is in order.



/* $Id: OLD-guile-faq.html,v 1.1 2000/07/25 19:17:37 ttn Exp $ */
#include <guile/gh.h>

SCM_PROC(s_remove_if_not, "remove-if-not", 1, 0, 1, scm_remove_if_not);
static SCM
scm_remove_if_not(SCM predicate, SCM args)
{
    SCM return_list = SCM_EOL;
    SCM_ASSERT(gh_procedure_p(predicate), predicate, SCM_ARG1, s_remove_if_not);
    for (; args != SCM_EOL; args = gh_cdr(args)) {
        if (gh_call1(predicate, gh_car(args)) != SCM_BOOL_F) {
            return_list = gh_cons(gh_car(args), return_list);
        }
    }
    return gh_reverse(return_list);
}

void
main_prog(int argc, char *argv[])
{
#   include "snarf.x"
    scm_shell(argc, argv);
}

int
main(int argc, char *argv[])
{
    gh_enter(argc, argv, (void(*)())main_prog);
    return(0);
}

Let's examine the SCM_PROC line in detail, using the primitive remove-if-not as an example. It says that there is a new primitive function with a Scheme name remove-if-not (argument 2), whose C implementation is the function scm_remove_if_not (argument 6). You can identify the C function in error messages with the constant string s_remove_if_not (argument 1).

The numeric argument in position three represents the number of required parameters. The signature of scm_remove_if_not should start with this many parameters of type SCM. In this case, there is one required parameter, named predicate. These parameters will always have valid Scheme data when the runtime calls your function.

The numeric argument in position four represents the number of optional parameters. The signature of scm_remove_if_not should contain, in addition to and following the parameters described previously, this many parameters. Each of these parameters may have the special value SCM_UNDEFINED, depending on whether the user supplied the parameter. In the case of remove-if-not, there are no optional parameters.

The numeric argument in position five represents a C boolean expression that specifies whether the function accepts a 'rest' list, which corresponds to the dot notation in a lambda list. The remove-if-not implementation above demonstrates this functionality. If the parameter is true (!= 0 in C parlance), then the signature for scm_remove_if_not should contain an additional parameter, appearing at the end of the parameter list. This final parameter will contain the user supplied list of additional arguments to the C primitive, or SCM_EOL if no list was supplied. In this case the parameter is called args.

guile> (remove-if-not odd? 1 2 3 4 5 6 7)
(1 3 5 7)


Node:Snarf setup Makefile.in, Previous:SCM_PROC, Up:Snarfing

Snarf setup in Makefile.in

The following Makefile.in bits are useful for generating the guile-snarf files from C source files. (FIXME: this needs to be expanded with an explanation of what is really happening here)

GUILE_SNARF = @GUILE_SNARF@
.SUFFIXES: .x
.c.x:
    $(GUILE_SNARF) $(CPPFLAGS) $< > $@


Node:Includes, Next:, Previous:Snarfing, Up:Building

Includes

You can find out the required C preprocessor arguments for compiling C files that use guile functions into object files using a utility program named guile-config that comes with guile, and should be installed in the same directory as guile. Here is an example usage:

[nytrdc058.eq.gs.com:~/src/lam/guile/doc ]% guile-config compile
-I/opt/guile/include

Complete documentation for guile-config comes with the guile reference manual, and I'd also recommend using See autoconf, to make this easier.


Node:Libraries, Next:, Previous:Includes, Up:Building

Libraries

You can find out the required libraries for linking guile into your program by running a utility program named guile-config that comes with guile, and should be installed in the same directory as guile. Here is an example usage:

[nytrdc058.eq.gs.com:~/src/lam/guile/doc ]% guile-config link
-L/opt/guile/lib -lguile -lqthreads -ldl -ltermcap -lsocket -lnsl -lm

Complete documentation for guile-config comes with the guile reference manual, and I'd also recommend using See autoconf, to make this easier.


Node:autoconf, Previous:Libraries, Up:Building

autoconf

If you're not using autoconf (see Top) when building your programs, you can skip this section, and you have my condolences. For the rest of you, this section will hopefully make your life a bit easier.

The first thing you need when using autoconf is configure.in, a file which directs autoconf how to construct configure. When using guile with autoconf, there are some special bits that you want to add to your configure.in:

GUILE_FLAGS
AC_PATH_PROG(GUILE_SNARF, guile-snarf)

The definition of the GUILE_FLAGS m4 macro comes from the guile distribution. To grab this macro definition, and put it in your project's aclocal.m4 file, run aclocal. If you've got guile installed somewhere other than the default place, you may need to specify a path to aclocal, like this.

aclocal --acdir=/opt/guile/share/aclocal

The GUILE_FLAGS defines two configure variables, GUILE_LDFLAGS and GUILE_CFLAGS, which will be of use in your Makefile.in.


Node:Modules, Next:, Previous:Building, Up:Top

Modules

guile provides facilities to split up your Scheme program into reusable components. Each component is called a module. Conventionally, a single file of Scheme code will define the contents of a particular module.

There are fuzzy plans to update guile's module system, however this has been the case for two years, so I will take some time to try to describe the current module system, since it might be around for a while yet.


Node:Scheme modules, Next:, Up:Modules

Scheme modules

guile allows the programmer to divide code into modules. A module is a special environment for resolving variable bindings. At any given time, one module is considered the current module, and this module can be retreived using the procedure current-module. You can get the name of a module using module-name.


Node:C modules, Next:, Previous:Scheme modules, Up:Modules

C modules

It is possible to write guile modules completely in C. This is useful if you're exposing a lot of code to Scheme, and you want to manage the visibility of various parts of the program.



/* $Id: OLD-guile-faq.html,v 1.1 2000/07/25 19:17:37 ttn Exp $ */

/* this code creates a new dynamically linkable module called (silly
   hack).  there is precisely one primitive defined in this module,
   '2+', which adds 2 to it's argument */

#include <guile/gh.h>

/* add new primitives here */
SCM_PROC(s_2_plus, "2+", 1, 0, 0, scm_2_plus);
static SCM
scm_2_plus(SCM x)
{
    SCM_ASSERT(gh_number_p(x), x, SCM_ARG1, s_2_plus);
    return(scm_sum(x, gh_int2scm(2)));
}

/* the init function */
void
scm_init_hack(void)
{
#   include "hack.x"
    return;
}

/* the pre-init function */
void
scm_init_silly_hack_module()
{
    scm_register_module_xxx("silly hack", (void*)scm_init_hack);
}


Node:Calling out to Scheme, Previous:C modules, Up:Modules

Calling out to Scheme

One of the most useful ways to use guile is to bolt it onto an existing C or C++ application, and then drop into the interpreter when some sort of dynamic behavior is required. In this case, the user would create a list of arguments, and then call a single Scheme function from C with this list of arguments to compute a result. This is called a callout into the Scheme interpreter.

The module system makes it non-trivial to make a callout to a procedure defined somewhere other than the the-root-module. So I often use the following utility function from C:



/* $Id: OLD-guile-faq.html,v 1.1 2000/07/25 19:17:37 ttn Exp $ */

/* it is possible to make a version of this that is much faster,
   i.e. that only does the gh_eval_str once. */
static SCM
make_callout(char *callout, SCM ls)
{
    SCM proc;
    char buf[512];
    sprintf(buf, "(module-ref (resolve-module '(guile-user)) '%s)", callout);
    proc = gh_eval_str(buf);
    if (proc == SCM_UNDEFINED) {
        printf("callout error: lookup failed for '%s'\n", callout);
        return SCM_UNDEFINED;
    } else {
        return scm_apply(proc, ls, SCM_EOL);
    }
}

/* example of how to use make_callout. first create a list of
   arguments using scm_listify.  note the SCM_UNDEFINED at the end of
   the argument list to scm_listify. */
void
query_popup_callout(long tag, int isOk)
{
    make_callout("fte:query-popup",
                 scm_listify(SCM_MAKINUM(tag),
                             (isOk? SCM_BOOL_T : SCM_BOOL_F),
                             SCM_UNDEFINED));
}


Node:Threads, Next:, Previous:Modules, Up:Top

Threads

guile supports an optional threaded programming model. By default guile does not include this thread support, but you can turn it on at configure time by specifying the --with-threads configure option.


Node:OS level threads, Next:, Up:Threads

OS level threads

guile includes a user level threads library called qthreads. At this time, guile does not support OS level threads. It is possible to use guile in a multi-threaded program, as long as you restrict all access to guile functions to a single thread. I don't think that it's possible to use guile from multiple threads, even if you wrap all uses of guile with a single mutex, because some data structures internal to guile could be left in an inconsistent state on a thread switch (I'm not sure about this part, someone please correct me if I'm wrong).

There is some work going on to make guile function nicely with OS level threads. Niibe Yutaka has done most of the work so far; a link to the patch and status information can be found on the temporary projects page http://home.thezone.net/~gharvey/guile/guile-projects.html.


Node:Thread primitives, Previous:OS level threads, Up:Threads

Thread primitives

If you want to use guile's user level threads library, don't forget to run configure with --with-threads. The following example programs should be helpful to illustrate what you can do with this threads library.


Node:Thread loop, Up:Thread primitives

Thread loop

This simple program starts a new thread, which simply goes into a loop of sleeping for one second, and then printing a message to the current output port. The original thread in the program does the same, and the two threads use a mutex variable to make sure that the output is not munged.

#!/opt/guile/bin/guile -s
!#

(define-module (guile-user))
;;; end-header

(define output
  (let ((output-mutex (make-mutex)))
    (lambda args
      (with-mutex output-mutex
        (for-each (lambda (arg) (write arg) (display " ")) args)
        (newline)
        (flush-all-ports)))))

(define (make-looper message)
  (lambda ()
    (let loop ()
      (select () () () 1)
      (output message)
      (loop))))

(let ((background (make-looper "background")))
  (begin-thread
   (background)))

(let ((foreground (make-looper "foreground")))
  (foreground))


Node:GC, Next:, Previous:Threads, Up:Top

GC

Garbage collection is a programming technique that provides the user with automatic storage management. The user does not need to figure out when a particular piece of memory is no longer used; the garbage collector provides this service. FIXME: links to GC sites should go here.

guile has a built-in garbage collector, which is used to implement the interpreter itself, but is also useful in the context of C or C++ programs that use guile.


Node:GC strategy, Next:, Up:GC

GC strategy

A conservative garbage collector is one that, instead of knowing the exact location of each object, discovers the set of live objects by scanning a region of memory, looking for areas that may be objects. The possible objects found may or may not actually be objects, but we are insured that all live objects referred to from that particular area are found by the garbage collector. Conservative collectors are most useful in 'hostile environments' (those where very little information on the types and locations of objects are provided); guile's merging of Scheme with C is a perfect example.

guile's method of collection is mostly precise. That is, most of the objects can be definitely traced by the GC, with some areas (the C stack & the stack of continuations) being scanned conservatively. This has some obvious benefits to C programmers, in that the programming style of interfacing with the Scheme facilities of guile is very similar to the style of using any other library.


Node:Conservative GC tradeoffs, Next:, Previous:GC strategy, Up:GC

Conservative GC tradeoffs

The benefits of a conservative GC:

  1. Very little is required of a programmer in the 'hostile environment' to use garbage collection. There's no explicit registration needed for objects.
  2. Less work can be expected per object. The amount of time required per GC is proportional to the size of the stack; benchmarking shows that this is not a problem in most cases. FIXME: this part got garbled from Greg's original doc somehow.
  3. Fewer hard to find bugs. Explicit registration/De-registration of roots is as close to menial labor as you can get when programming. As with working on an assembly line, doing boring, repetitive things long enough usually has horrible results (thankfully, guile currently doesn't have any sharp, moving objects).

The drawbacks of a conservative GC:

  1. Areas of memory that look like pointers to objects, but aren't, cause garbage objects to be retained as long as the fake pointer exists. This increases memory usage of the garbage collector, and can cause other limited resources to be exhausted.
  2. The methods of finding exactly which areas to trace isn't always portable.

In the worst case of the conservative collector, the program fails due to a resource exhausting number of dead objects being retained by incorrect pointers.

This worst case scenario, like many worst case scenarios, is unlikely for typical programs. Heavy recursion is the most likely candidate for causing things to go horribly wrong; for the most part, this is purposely avoided in languages like C for performance reasons. A more insidious candidate for failure is the use of the stack for keeping large amounts of static program state; this also doesn't generally happen (given that alloca is non-standard, and not always well implemented). Regardless of their unlikeliness, though, these both suggest suggest that programmers must at least be mindful of the existence and limitations of the conservative garbage collector, in order to notice areas where problems could occur.


Node:Explicit marking, Previous:Conservative GC tradeoffs, Up:GC

Explicit marking

A way to get around the uncertainty of the conservative garbage collector is to introduce a system whereby the programmer explicitly registers and De-registers GC-able objects with the garbage collector. This allows us to be 100% certain that no dead objects are falsely considered live due to non-pointers looking like pointers, as well as allowing various other improvements, including the movement of stack traced objects.

The biggest drawback of explicit marking is the amount of manual bookkeeping the programmer has to do. Although easy in theory, in real programs, bugs tend to gravitate towards the bits where you have to do a lot of repetitive manual calculations (off by one arrays, malloc/free memory leaks, etc...). While compilers and other automatic tools can provide diagnostics for most of these problems, the fact remains that large, non-trivial programs tend to do non-trivial things, and programming tools can't find all of the areas that programs can go wrong.

There is also the issue of what to do in the presence of constructs like longjmp; in many programs, this is often not a worry; in Guile, however, many of the constructs are rolled around this, and it's hard to see a nice way of insuring that objects are handled correctly in their presence (or to see how the author of a Scheme program can do things correctly without having to know how the details of how everything in guile works).

Explicit marking insures that no garbage is falsely retained. It also brings us close enough to the malloc/free situation, that we might as well just say screw it and use explicit allocation. Unless a way can be shown to have explicit marking without the bookkeeping being placed on the heads of programmers, I'll continue to take the slightest possibilities of failing programs over the much higher probability of failing programmers.


Node:Recipes, Next:, Previous:GC, Up:Top

Recipes

This section attempts to present a number of small, but useful examples of how to use guile to solve common problems. At least one of the programs contained here is actually used to prepare this document, in fact.


Node:File by lines, Next:, Up:Recipes

File by lines

The following code shows how to read and process lines one at a time from the file foo. guile's IO performance is not very fast at the moment, so if you have to process large files, you may want to use a different model than the code below.

(with-input-from-file "foo"
  (lambda ()
    (while (not (eof-object? (peek-char)))
      (display (read-line))
      (newline))))


Node:slib, Next:, Previous:File by lines, Up:Recipes

slib

There is a portable library of Scheme functions, called slib, that is very useful when writing non-trivial programs. Aubrey Jaffer put together slib in conjunction with SCM, a Scheme implementation which is a precursor to guile (many people still prefer SCM, actually (doh!)).

Before you can use slib with guile, you need to grab the tarball from http://www-swiss.ai.mit.edu:80/~jaffer/SLIB.html and install it. I recommend installing slib in the $prefix/share/guile/site directory, so that when you upgrade guile, you don't have to reinstall slib. Installing slib should be a simple matter of unpacking the files, but read the installation notes in the package to be sure.

Once slib is installed, to use slib within a module definition, put something like the following in the module definition:

(define-module (silly hack)
    :use-module (ice-9 slib))
(require 'format)

To use slib within an arbitrary piece of scheme code, precede the code with:

(use-modules (ice-9 slib))
(require 'format)


Node:Daemon, Next:, Previous:slib, Up:Recipes

Daemon

It's not difficult to use guile two write Unix style daemon processes. This demonstration program is thanks to Gary Houston. If you get ambitious, extend this little program to use guile's threading capability, and send me the result.


;;;
;;; From: Gary Houston <ghouston@actrix.gen.nz>
;;; Subject: Re: Socket programming
;;; To: szi@aibon.ping.de
;;; Cc: guile@cygnus.com
;;; Date: 30 Sep 1997 23:17:27 -0000
;;;
;;; | Date: Tue, 30 Sep 1997 21:34:27 +0100
;;; | From: Sascha Ziemann <szi@aibon.ping.de>
;;; |
;;; | Hi,
;;; |
;;; | the Perl book contains a example program that shows how to write a
;;; | simple daemon. Does such an example exist for Guile too?
;;; |
;;;
;;; Here's a complicated way to write a simple daemon.  talk to it by
;;; telnetting to port 20004:

(let ((port (socket AF_INET SOCK_STREAM 0))
      (inet-port 20004))          ; Some unused port.
  (bind port AF_INET INADDR_ANY inet-port)
  (listen port 10)                ; Queue up to 10 requests.
  (let loop ()
    (let ((p (accept port)))
      (display "Incoming connection from ")
      (display (inet-ntoa (sockaddr:addr (cdr p))))
      (display " port ")
      (display (sockaddr:port (cdr p)))
      (newline)
      (let next-line ()
          (let ((line (read-line (car p))))
             (cond ((eof-object? line)
                    (display "EOF\n"))
                   (else
                    (display line)
                    (newline)
		    (display "you sent:\n" (car p))
		    (display line (car p))
		    (newline (car p))
                    (next-line)))))
      (close-port (car p))
      (loop))))


Node:texinfo-protect, Next:, Previous:Daemon, Up:Recipes

texinfo-protect

This program is used in the preparation of the guile FAQ. The program accepts a filename as an argument, and prints to standard output the contents of the file, with texinfo special characters prefixed with @.

This program is probably a one liner in perl (doh!). Of course, if I rewrote it using the scsh awk macro, I'm sure it would be much shorter. substitute-global type-functionality should definitely be included somewhere, for certain.


#! /home/ttn/local/bin/guile -s
!#

(define-module (guile-user)
  :use-module (ice-9 regex))
;;; end-header

(define (map-filename filename)
  (string-append filename "-txt"))

(define (process-file filename)
  (with-output-to-file (map-filename filename)
    (lambda ()
      (with-input-from-file filename
	(lambda ()
	  (while (not (eof-object? (peek-char)))
		 (regexp-substitute/global
		  (current-output-port) "[{}@]" (read-line)
		  'pre "@" (lambda (m) (match:substring m 0)) 'post)
		 (newline)))))))

(for-each process-file (cdr (command-line)))


Node:SCM to C string, Next:, Previous:texinfo-protect, Up:Recipes

SCM to C string

Here's a way to get a C string containing the printed representation of an object of type SCM. (Thanks to Westley Sherman). The user of the function is responsible for freeing the returned string. As an optimization, the stringify function could be eval'ed just once, stored away, and protected with scm_permanent_object.

char *
scm2cstring(SCM object, int *length_p) {
  SCM stringify;
  char * string;
  stringify = gh_eval_str("(lambda(object)"
                          " (with-output-to-string"
                          "  (lambda() (write object))))");
  string = gh_scm2newstr(gh_call1(stringify, object), length_p);
  return string;
}


Node:Custom catch, Previous:SCM to C string, Up:Recipes

Custom catch

It is often useful to display errors to the user, and the quality of the error message is very important. Applications may want to display error messages in a window, or in some other application specific way, so the user must have some way or writing a custom error handler. Fortunately guile supports this, and we've included some example code below.

This code is more useful than is should be, though, because guile's error handling our of the box works well only when you're running. When the C world is in control, guile tends to generate terse error messages, which are hard to make sense out of. Using the code below, you can get at the full richness of guile's error handling facilities, including stack traces.

Thanks to Eric Ludlam for contributing the guts of this code.

File catch.c:


// $Id: OLD-guile-faq.html,v 1.1 2000/07/25 19:17:37 ttn Exp $
// Author: Russ McManus

// guile includes
#include <guile/gh.h>
#include <gfec.h>

static void
prompt()
{
  gh_eval_str("(display \"catch> \")"
              "(force-output)");
}

static void
error_handler(const char *err_str)
{
  printf("----------------\n");
  printf("%s\n", err_str);
  printf("----------------\n");
}

static void
read_eval_print()
{
  SCM thunk = gh_eval_str("(lambda ()"
                          "  (display (eval (read)))"
                          "  (newline))");
  prompt();
  while (1)
    {
      gfec_apply(thunk, SCM_EOL, error_handler);
      prompt();
    }
  return;
}

void
main_prog(int argc, char *argv[])
{
  read_eval_print();
}

int
main(int argc, char *argv[])
{
  gh_enter(argc, argv, (void(*)())main_prog);
  return(0);
}

File gfec.c:



/* $Id: OLD-guile-faq.html,v 1.1 2000/07/25 19:17:37 ttn Exp $
 *  Authors: Eric M. Ludlam <zappo@ultranet.com>
 *           Russ McManus <russell.mcmanus@gs.com>
 *
 *  gfec stands for 'guile fancy error catching'.  This code is in the
 *  public domain.
 */

#include <gfec.h>
#include <string.h>

#ifdef __cplusplus
extern "C"
{
#endif

#include <libguile/fluids.h>

#ifdef __cplusplus
}
#endif


/* we assume that data is actually a char**.  the way we return
   results from this function is to malloc a fresh string, and store
   it in this pointer.  it is the caller's responsibility to do
   something smart with this freshly allocated storage. the caller can
   determine whether there was an error by initializing the char*
   passed in to NULL.  if there is an error, the char string will not
   be NULL on return.*/
static SCM
gfec_catcher(void *data, SCM tag, SCM throw_args)
{
  SCM the_stack;
  /* create string port into which we write the error message and
     stack. */
  SCM port = scm_mkstrport(SCM_INUM0,
			      scm_make_string(SCM_MAKINUM(200), SCM_UNDEFINED),
			      SCM_OPN | SCM_WRTNG,
			      "error-handler");
  /* throw args seem to be: (FN FORMAT ARGS #f). split the pieces into
     local vars. */
  if (gh_list_p(throw_args) && gh_length(throw_args) >= 4)
    {
      SCM fn = gh_car(throw_args);
      SCM format = gh_cadr(throw_args);
      SCM args = gh_caddr(throw_args);
      SCM other_data = gh_car(gh_cdddr(throw_args));

      if (fn != SCM_BOOL_F)
        { /* display the function name and tag */
          scm_puts("Function: ", port);
          scm_display(fn, port);
          scm_puts(", ", port);
          scm_display(tag, port);
          scm_newline(port);
        }

      if (gh_string_p(format))
        { /* conditionally display the error message using format */
          scm_puts("Error: ", port);
          scm_display_error_message(format, args, port);
        }
      if (other_data != SCM_BOOL_F)
        {
          scm_puts("Other Data: ", port);
          scm_display(other_data, port);
          scm_newline(port);
          scm_newline(port);
        }
    }

  /* find the stack, and conditionally display it */
  the_stack = scm_fluid_ref(SCM_CDR(scm_the_last_stack_fluid));
  if (the_stack != SCM_BOOL_F)
    {
      scm_display_backtrace(the_stack, port, SCM_UNDEFINED, SCM_UNDEFINED);
    }

  SCM_DEFER_INTS;
  /* apparently the car of the stream object is
     the length of the string, and the cdr is the string itself. */
  *(char**)data = strdup(SCM_CHARS(SCM_CDR(SCM_STREAM(port))));
  SCM_ALLOW_INTS;
  return gh_int2scm(1);
}

/* the arguments to scm_internal_stack_catch:
   ------------------------------------------
   SCM tag                     : this should be SCM_BOOL_T to catch all errors.
   scm_catch_body_t body       : the function to run.
   void *body_data             : a pointer to pass to body
   scm_catch_handler_t handler : the hander function
   void *handler_data          : a pointer to pass to the handler
   */

/* what is the return value of a catch function used for? */

void
gfec_eval_file(const char *file, gfec_error_handler error_handler)
{
  char *err_msg = NULL;
  scm_internal_stack_catch(SCM_BOOL_T,
                           (scm_catch_body_t)gh_eval_file, (void*)file,
                           (scm_catch_handler_t)gfec_catcher, (void*)&err_msg);
  if (err_msg != NULL)
    {
      error_handler(err_msg);
      free(err_msg);
    }
  return;
}

void
gfec_eval_string(const char *str, gfec_error_handler error_handler)
{
  char *err_msg = NULL;
  scm_internal_stack_catch(SCM_BOOL_T,
                           (scm_catch_body_t)gh_eval_str, (void*)str,
                           (scm_catch_handler_t)gfec_catcher, (void*)&err_msg);
  if (err_msg != NULL)
    {
      error_handler(err_msg);
      free(err_msg);
    }
  return;
}

typedef struct gfec_apply_rec
{
  SCM proc;
  SCM arglist;
};

static void
gfec_apply_helper(void *data)
{
  struct gfec_apply_rec *apply_rec = (struct gfec_apply_rec *)data;
  gh_apply(apply_rec->proc, apply_rec->arglist);
}

void
gfec_apply(SCM proc, SCM arglist, gfec_error_handler error_handler)
{
  char *err_msg = NULL;
  struct gfec_apply_rec apply_rec;
  apply_rec.proc = proc;
  apply_rec.arglist = arglist;
  scm_internal_stack_catch(SCM_BOOL_T,
                           (scm_catch_body_t)gfec_apply_helper, (void*)&apply_rec,
                           (scm_catch_handler_t)gfec_catcher, (void*)&err_msg);
  if (err_msg != NULL)
    {
      error_handler(err_msg);
      free(err_msg);
    }
  return;
}

File gfec.h:


// $Id: OLD-guile-faq.html,v 1.1 2000/07/25 19:17:37 ttn Exp $
// Author: Russ McManus

#ifndef GFEC_H
#define GFEC_H

#include <guile/gh.h>
typedef void (*gfec_error_handler)(const char *error_message);
void gfec_eval_file(const char *file, gfec_error_handler error_handler);
void gfec_eval_string(const char *str, gfec_error_handler error_handler);
void gfec_apply(SCM proc, SCM arglist, gfec_error_handler error_handler);

#endif


Node:Common Problems, Next:, Previous:Recipes, Up:Top

Common Problems

Stuff about common problems goes here.


Node:Concept Index, Previous:Common Problems, Up:Top

Concept Index