Next Previous Contents

2. Introduction

SLIRP, the (SL)ang (I)nte(R)face (P)ackage, is a vectorizing code generator aimed primarily at simplifying the process of creating modules for the S-Lang scripting language. It can dramatically reduce the time and effort required to make C, C++, or Fortran code callable directly from the S-Lang interpreter, automatically vectorize functions to take advantage of the powerful numerical and array capabilities native to S-Lang, generate parallelizable wrappers for OpenMP-aware compilers, and even generate Makefiles to automate the build process. SLIRP may also be used to generate pure C bindings for C++ code, or empty (stub) implementations for the interface specified by its input. These features are more general in nature and the emitted code has no dependencies upon S-Lang whatsoever. SLIRP has also been directly or indirectly employed in the publication of a number of scientific papers.

2.1 Brief History

SLIRP grew out of an effort by the author to develop SLgtk, the S-Lang bindings to the Gimp Toolkit (more popularly known as Gtk). To it's credit the Gtk development community has paid close attention to the problem of binding Gtk to other languages, and mechanisms which considerably aid that process are readily available. Oddly enough, many Gtk language bindings do not use SWIG, but instead rely upon so-called .defs files, which are descriptions -- in the Scheme language -- of the apis of the underlying libraries, apparently generated from the header files and then supplemented with additional semantics.

In typical hacker fashion the SLgtk bindings project began by building upon the work of someone else, which in this case amounted to borrowing the .defs files from PyGtk and Gtk itself. While this proved a useful starting point, over time enough undocumentedness/inconsistencies/omissions were encountered to undermine my confidence, so I opted to utilize the .defs files when they provided information not readily available in header files (e.g., to identify functions which accept NULL for one or more arguments), and wrote a minimal generator to fabricate the bulk of the bindings directly from the Gtk headers. After all, even though the source may not tell the complete story it certainly doesn't forget and never lies. Relying too heavily upon .defs files could also limit the potential scope of the generator, making it too specialized to be used on other libraries which do not describe their apis in such fashion.

Given that SWIG currently does not support S-Lang a logical question to ask is why I did not write a S-Lang extension for SWIG and use it to generate SLgtk? Well, part of the answer lies above, namely that if Gtk were "more SWIG-able" then presumably more developers would be using SWIG for Gtk bindings generation. Minimizing external dependencies was another significant motivation; SLIRP is 99% pure S-Lang code, and about 1% C, so if you've got S-Lang and a C compiler installed (e.g., as millions of Linux users do by default) you can use SLIRP immediately, without installing any additional packages. Other factors included having greater control over the generated code, as well as providing the ability to support Fortran, generate vectorized wrappers (optionally parallelized with OpenMP for multiprocessors), and generate Makefiles, none of which were offered by SWIG.

2.2 Installation

The remainder of this manual assumes you have installed SLIRP as outlined in the README at the top of the distribution. A number of sample modules are provided in the examples subdirectory.

2.3 The kitchensink Module

As our first example, consider a library named kitchensink whose public interface ksink.h contains


    typedef struct { char *name; double value; } KDatum;
    typedef struct { double p[3]; unsigned long id; } KParams;

    typedef enum { KSINK_GOOD=0, KSINK_BAD, KSINK_UGLY, KSINK_HORRIFIC } KErrorCode;

    extern      long            ksink_sum               (long augend, long addend);
    extern      double          ksink_mult              (double op1, double op2);

    extern      KDatum          *ksink_datum_new        (char *name, double value);
    extern      KParams*        ksink_params_new   (unsigned long id, double params[3]);
    extern      const char*     ksink_params_str        (KParams *p);
    extern      void            ksink_datum_destroy     (KDatum *datum);
    extern      void            ksink_set_ref_i         (int *i);

    extern      void            ksink_print_array_f     (long nelems, float array [  ]);
    extern      char**          ksink_make_array_s      (void);
    extern      void            ksink_print_array_s     (long   nelems, char **array);
    extern      char**          ksink_make_array_nts    (void);
    extern      void            ksink_print_array_nts   (char **array);

    extern      void            ksink_print_error       (KErrorCode err);

    extern      void            ksink_swap_double       (double *i, double *j);
    extern      void            ksink_close             (int fd);
    extern      FILE*           ksink_fopen             (char *name, char *mode,
                                                unsigned long  *size,
                                                unsigned short *nlinks);
Further suppose that one day that you misplaced your wits and decided it was absolutely necessary that you be able to call this library from a S-Lang script. How would you go about such? This is the problem SLIRP helps solve. Let's begin by issuing the command
        unix% slirp ksink.h
which will generate, amongst others, the wrapper
        static void sl_ksink_mult (void)
        {
           double result;
           double arg1;
           double arg2;

           if (SLang_Num_Function_Args != 2 ||
                SLang_pop_double(&arg2) == -1 ||
                SLang_pop_double(&arg1) == -1 )
                {Slirp_usage_err("double = ksink_mult(double,double)"); return;}

           result = ksink_mult(arg1, arg2);
           (void) SLang_push_double ( result);
        }
SLIRP provides a number of ways of tailoring its operation and output, but in this instance the code is emitted to a file named ksink_glue.c in the current working directory. Next we need to make the S-Lang interpreter aware of this wrapper by installing it as a so-called intrinsic function. There's more than one way to do this, but the best practice is to create an entry for each wrapper within an intrinsic function table, and register all of them at once with a single call to either SLadd_intrin_fun_table() or its namespace variant SLns_add_intrin_fun_table(). To assist the language binder with this SLIRP also generates an intrinsic function table entry for each each wrapper function that it generates. In our example this entry looks like
        MAKE_INTRINSIC_0("ksink_mult",sl_ksink_mult,V),
and is also emitted to ksink_glue.c. Now all that remains is to provide a means of binding the generated code to the S-Lang interpreter at runtime. For this we need another code fragment to initialize the module, something along the lines of
        #include "slang.h"
        #include "ksink.h"
        
        static SLang_Intrin_Fun_Type ksink_Intrin_Funcs[] =
        {
          MAKE_INTRINSIC_0("ksink_mult",sl_ksink_mult,V),
          ...
          SLANG_END_INTRIN_FUN_TABLE
        };
        
        SLANG_MODULE(ksink);
        int init_ksink_module_ns(char *ns_name)
        {
           SLang_NameSpace_Type *ns = NULL;
        
           if (ns_name != NULL) {
                ns = SLns_create_namespace (ns_name);
                if (ns == NULL)
                   return -1;
           }
        
           if (-1 == SLns_add_intrin_fun_table (ns, ksink_Funcs, "__ksink__"))
                return -1;
        
           return 0;
        }
Typically the file generated by SLIRP will be compiled via Makefile rules, to build an importable module which may be accessed at runtime via the S-Lang import() function. In our case this would instruct the S-Lang runtime to dynamically load a shared object library named ksink-module.so, invoke its init_ksink_module_ns() initializer, and register the wrapper functions, constants, and variables defined within.

Here's how our ksink_mult wrapper might be used within a S-Lang script:

        import("ksink");

        define do_mult(op1, op2)
        {
           print("ksink_mult(%S, %S) = %S", op1, op2, ksink_mult(op1,op2));
        }

        do_mult(333, 3);
        do_mult(PI/2, 2);
For more details consult the code for this example, which is located in the examples/kitchensink directory of the SLIRP distribution and can be built and invoked by typing make followed by make demo at the command line. The modules directory within the S-Lang source distribution also contains a number of useful modules, as does the MIT modules page at http://space.mit.edu/cxc/software/slang/modules/.

2.4 Expectations

It is important to realize that SLIRP is no silver bullet. In the abstract code generators are not meant to free the programmer from the responsibility of thinking at all, but rather aim at freeing them to think about only that which requires substantive thought, leaving the rest of the presumably mundane details to be mechanically churned out by a machine.

For a variety of reasons the developer may prefer to wrap portions of a library manually, instead of relying upon wrappers emitted by a generator. As such most bindings projects will likely include both generated and hand-crafted code, so it is pragmatic to view a code generator as but one element in the toolbox of the language binder.

It should also be noted that SLIRP does not fully preprocess, nor is it a compiler for, the C language. By choosing a S-Lang-based implementation (instead of one, say, in C, generated from a grammar specified in Lex/Yacc) we exchange completeness for size, simplicity of design and distribution, ease of use, and portability. However, as discussed in section Pointers, even if SLIRP contained a full C preprocessor or compiler it would still be impossible for it to mechanically map every possible construct from the universe of syntactically valid C libraries to an equivalent construction in S-Lang. For these reasons code generators provide mechanisms to customize their behavior and layer additional semantics on top of the underlying api (such as the .defs mechanisms described earlier, or the interface files respectively employed by SWIG and SLIRP). Nevertheless, SLIRP is known to generate useful bindings for numerous C, C++, and Fortran codes, including SLgtk, OpenGL, MySQL, PVM, libGlade, glibc (gettext), HDF5, NetCDF, cgilib, ASURV, and volpack.


Next Previous Contents