Calling C functions from Guile

The first thing that you should be asking, once you've seen the title of this section, is why? Why would you want to be calling C functions from Guile if you are using Guile to extend your C programs?

There actually is a good reason for doing this other than showing one's elite hacker skills. One can diving the C program into modular blocks of code, and then use Guile to arrange their interactions. This way, at the top level, the program can be easy to rearrange and prototype.

While calling Guile functions from C was straightforward enough, calling C functions from Guile is tougher. It is more difficult because of all the type checking that must occur.

If you want to wrap a lot of C functions for use with Guile, you should seriously consider the g-wrap framework or swig application, at http://www.nongnu.org/g-wrap. It is a middle layer that helps wrap C functions for use with Guile (and possibly other languages).

In C, at compile time, if the type or number of parameters being passed to a function is incorrect, the compiler will register an error and stop. With Guile, one is free to call a function passing any type or number of parameters. It is the responsibility of programmer or the function to ensure the parameters it is passed are correct.

To create C functions that can be called by Guile, one must do these things.

Guile has a set of macros to help with the second and third step, so the process will be described twice. First I'll describe how to do it manually. Then, I'll describe how to use tools and macros to help with this process.

Manually Creating a C Function for Guile

For a function to be exportable to Guile it must only take parameters of type SCM and must return a value of type SCM. Parameters of the standard C types are not allowed.

Then the function must be registered so that Guile can find it. The functions that registers a new Guile function written in C is scm_c_define_gsubr.

SCM scm_c_define_gsubr(const char *name, int req, int opt, int rst, (SCM (*)()) func);

The function scm_c_define_gsubr takes the C function func and registers it as a Guile function with the name name. The function's name in Guile (name) and its name in C (func) do not have to be the same. req is the function's number of required input parameters. opt is its number of optional parameters. rst is a flag that indicates if the function takes a variable number of inputs.

Functions with only required parameters

We'll start off with the most common case: functions with no optional parameters and no variable number of arguments.

Consider the following C function. It takes one input parameter, a C string, and returns one output parameter, a newly allocated C string.

Example 5. html_entities: a C funtion with 1 required parameter.

#include <assert.h>
#include <stdlib.h>
#include <string.h>


/* This function converts the characters < > & and quotation marks
 * into their html entities, which are &lt; &gt; &amp; and &quot; */
char *html_entities (char *raw_text)
{
     int raw_length;
     int out_length;
     char *out_text;
     char new_char[2];
     int i;

     assert (raw_text != (char *)NULL );
     
     raw_length = strlen (raw_text);
     
     if (raw_length == 0)
     {
	  out_text = malloc(sizeof(char));
	  out_text[0] = '\0';
     } 
     else
     {
	  /* Count the number of characters needed for the output
	   * string */
	  out_length = 0;
	  for (i = 0; i < raw_length; i++)
	  {
	       switch (raw_text[i]) {
	       case '<':
	       case '>':
		    out_length += 4;
		    break;
	       case '&':
		    out_length += 5;
		    break;
	       case '"':
		    out_length += 6;
		    break;
	       default:
		    out_length += 1;
	       }
	  }

	  /* Convert some tags into their HTML entities */
	  out_text = (char *) malloc ((out_length + 1) * sizeof(char));
 	  out_text[0] = '\0';
	  for (i = 0; i < raw_length; i++)
	  {
	       switch (raw_text[i]) {
	       case '<':
		    strcat(out_text, "&lt;");
		    break;
	       case '>':
		    strcat(out_text, "&gt;");
		    break;
	       case '&':
		    strcat(out_text, "&amp;");
		    break;
	       case '"':
		    strcat (out_text, "&quot;");
		    break;
	       default:
		    new_char[0] = raw_text[i];
		    new_char[1] = '\0';
		    strcat (out_text, new_char);
		    break;
	       }
	  }
     }

     return (out_text);
}

	  

This function does a bit of mundane HTML processing, namely converting some dodgy characters like "<" into their safer HTML entities like &lt;.

To make this into a Guile function, a wrapper function has to be written so that instead of a function that takes a C string and returns a C string, it becomes a function that takes an SCM and returns and SCM.

The wrapper function could look like this.

Example 6. html_entities: a wrapper for the C funtion

#include <stdlib.h>

#include <libguile.h>
#include <guile/gh.h>

#include "html_entities.h"

/* This function does the SCM packing and unpacking to convert
 * html-entities() into a Guile function */
SCM scm_html_entities (SCM scm_raw_text)
{
     SCM scm_out_text;
     char *raw_text;
     char *out_text;
     int length;

     /* Check that the input is a string */
     SCM_ASSERT (SCM_STRINGP(scm_raw_text), 
		 scm_raw_text, 
		 SCM_ARG1, 
		 "html-entities");
     
     /* Convert the Guile string to a C string */
     raw_text = gh_scm2newstr (scm_raw_text, &length);

     /* Call the C function */
     out_text = html_entities(raw_text);

     /* Convert the C function's output to an SCM */
     scm_out_text = scm_makfrom0str (out_text);

     free (out_text);
     return (scm_out_text);
}

	  

Note that the the function checks the input parameter to ensure that it is a string.

Now that the function is ready for use by Guile, the following code will register it as a Guile function with one required parameter and no optional parameters or variable argument lists. The Guile name of this function will be html-entities.

     scm_c_define_gsubr 
	  ("html-entities", 1, 0, 0, (SCM (*)()) scm_html_entities); 
	

This function call needs to occur after the call to scm_init_guile and before the new Guile function html-entities is first used.

Here is a code snippet using the new function.

(display 
 (html-entities "See my picture <a href=\"pics/me.jpg\">here</a>"))
(newline)

	

Running the script would create the following output

See my picture &lt;a href=&quot;pics/me.jpg&quot;&gt;here&lt;/a&gt;

	

Functions with optional parameters

So what is the difference between an optional parameter and a variable number of arguments?

Functions with variable numbers of arguments are like C's printf. printf has one required argument, the format, and a variable number of additional parameters. In C, these additional parameters have no unique names and are just dumped into a list that can be accessed using the va_arg macros.

For functions with optional parameters, the optional parameters are not dumped into an anonymous list. Each optional parameter has a unique name. This is much like C++ declarations where the rightmost parameters in a function call can be left off. But in C++ the function declaration sets the default values of optional parameters. In C functions with optional parameters imported into Guile, the default value of optional parameters is always SCM_UNDEFINED.

In this next example, there is a C function that has one parameter, a flag. The Guile version of that function will take that one parameter as an optional parameter, and if it is neglected, will revert to a default behavior.

For the function to know whether or not its parameter was explicitly set or not, it tests to see if its parameter has the value of SCM_UNDEFINED. If it does, the function knows that value of the optional parameter has been left unset. To test the value to see if it undefined, it uses the SCM_UNBNDP macro.

int SCM_UNBNDP(SCM arg);

The SCM_UNBNDP returns non-zero if the value of arg is SCM_UNDEFINED.

Here is a program that defines and registers a C function as a Guile function with one optional parameter. The function make_date_string gives the local time if the flag is TRUE or GMT time if the flag is FALSE. The function s_make_date_string is a wrapper to package the inputs and outputs as SCMs. s_make_date_string is registered as a Guile function called time-now by calling scm_c_define_gsubr

Example 7. An Optional Parameter: main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libguile.h>

char *make_date_string (int is_local);
SCM s_make_date_string (SCM is_local);

int main(int argc, char **argv)
{

     /* initialize Guile */
     scm_init_guile();

     /* Register the new C-Guile functions */
     scm_c_define_gsubr 
	  ("time-now", 0, 1, 0, (SCM (*)()) s_make_date_string); 

     /* Load the library of Guile functions */
     scm_c_primitive_load("script.scm");

     return 0;
}

SCM s_make_date_string (SCM is_local)
{
  SCM date_string;
  char *out_text;

  if (SCM_UNBNDP (is_local) || SCM_FALSEP (is_local)) {
    out_text = make_date_string (0);
  } else {
    out_text = make_date_string (1);
  }

  /* Convert the C function's output to an SCM */
  date_string = scm_makfrom0str (out_text);
  
  free (out_text);
  
  return date_string;
}

char *make_date_string (int is_local)
{
  struct tm *p_tm;
  char buf[128];
  size_t len;
  char *answer;
  time_t t;

  time(&t);
  
  if (is_local) {
    p_tm = localtime (&t);
    len = strftime (buf, sizeof (buf), "%a %b %e %H:%M:%S %Z %Y", p_tm);
  } else {
    p_tm = gmtime (&t);
    len = strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S GMT", p_tm);
  }

  answer = strdup (buf);
  return answer;
}


	  

Here is a script that calls the function with and without its optional parameter.

Example 8. An Optional Parameter: script.scm

(display 
 (time-now))
(newline)
(display 
 (time-now #t))
(newline)
(display 
 (time-now #f))
(newline)


	  

Running the script would create the following output.

2004-07-19 05:26:25 GMT
Sun Jul 18 22:26:25 PDT 2004
2004-07-19 05:26:25 GMT