git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@444 626c5289-ae23-0410-ae9c-e8d60b6d4f22
1084 lines
39 KiB
HTML
Executable file
1084 lines
39 KiB
HTML
Executable file
<html>
|
|
<title> SWIG : An Easy to Use Tool For Integrating Scriptint Languages
|
|
with C and C++ </title>
|
|
<body bgcolor="#ffffff">
|
|
<h1>
|
|
SWIG : An Easy to Use Tool for Integrating Scripting Languages with C and C++
|
|
</h1>
|
|
|
|
<b> David M. Beazley </b> <br>
|
|
<em> Department of Computer Science <br>
|
|
University of Utah <br>
|
|
Salt Lake City, Utah 84112 <br>
|
|
beazley@cs.utah.edu <br> </em>
|
|
|
|
<br> <br>
|
|
(Presented at the 4th Annual Tcl/Tk Workshop, Monterey, CA. July 6-10, 1996. )
|
|
|
|
<h2> Abstract </h2>
|
|
|
|
<em>
|
|
I present SWIG (Simplified Wrapper and Interface Generator), a
|
|
program development tool that automatically generates the bindings
|
|
between C/C++ code and common scripting languages including
|
|
Tcl, Python, Perl and Guile. SWIG supports most C/C++ datatypes
|
|
including pointers, structures, and classes.
|
|
Unlike many other approaches, SWIG
|
|
uses ANSI C/C++ declarations and requires the user to make virtually
|
|
no modifications to the underlying C code.
|
|
In addition, SWIG automatically
|
|
produces documentation in HTML, LaTeX, or ASCII format.
|
|
SWIG has been primarily designed for scientists, engineers, and application
|
|
developers who would like to use scripting languages with their C/C++ programs
|
|
without worrying about the underlying implementation details
|
|
of each language or using a complicated software development tool.
|
|
This paper concentrates on SWIG's use with Tcl/Tk.
|
|
</em>
|
|
|
|
<h2> 1. Introduction </h2>
|
|
SWIG (Simplified Wrapper and Interface Generator)
|
|
is a software
|
|
development tool that I never intended to develop. At the time,
|
|
I was trying to add a data analysis and visualization capability to
|
|
a molecular dynamics (MD) code I had helped develop for massively
|
|
parallel supercomputers at Los Alamos National Laboratory [Beazley,Lomdahl]. I wanted
|
|
to provide a simple, yet flexible user interface that could be used
|
|
to glue various code modules together and an extensible scripting
|
|
language seemed like an ideal solution. Unfortunately there were constraints.
|
|
First, I didn't want to hack up 4-years of code development trying to
|
|
fit our MD code into yet another interface scheme (having done so several
|
|
times already). Secondly, this code
|
|
was routinely run on systems ranging from Connection Machines and Crays to
|
|
workstations and I didn't want to depend on any one
|
|
interface language---out of fear that it might not be supported on
|
|
all of these platforms. Finally, the users were constantly adding
|
|
new code and making modifications. I needed a flexible, yet easy to use
|
|
system that did not get in the way of the physicists. <br> <br>
|
|
|
|
SWIG is my solution to this problem. Simply stated, SWIG automatically generates all
|
|
of the code
|
|
needed to bind C/C++ functions with scripting languages using only
|
|
a simple input file containing C function and variable declarations.
|
|
At first, I supported
|
|
a scripting language I had developed specifically for use
|
|
on massively parallel systems. Later I decided to rewrite SWIG in C++ and extend it to support
|
|
Tcl, Python, Perl, Guile and other languages that interested me. I also
|
|
added more data-types, support for pointers, C++ classes, documentation generation, and
|
|
a few other features. <br> <br>
|
|
|
|
This paper provides a brief overview of SWIG
|
|
with a particular emphasis on Tcl/Tk. However, the reader should
|
|
remain aware that SWIG works equally well with
|
|
Perl and other languages. It is not my intent to provide
|
|
a tutorial or a user's guide, but rather to show
|
|
how SWIG can be used to do interesting things such as
|
|
adding Tcl/Tk interfaces to existing C applications,
|
|
quickly debugging and prototyping C code,
|
|
and building interface-language-independent C applications.
|
|
|
|
<h2> 2. Tcl and Wrapper Functions </h2>
|
|
|
|
In order to add a new C or C++ function to Tcl, it is necessary to
|
|
write a special ``wrapper'' function that parses the function arguments
|
|
presented as ASCII strings by the Tcl interpreter into a representation
|
|
that can be used to call the C function. For example, if you wanted
|
|
to add the factorial function to Tcl, a wrapper function might
|
|
look like the following : <br>
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
int wrap_fact(ClientData clientData,
|
|
Tcl_Interp *interp,
|
|
int argc, char *argv[]) {
|
|
int result;
|
|
int arg0;
|
|
if (argc != 2) {
|
|
interp->result = "wrong # args";
|
|
return TCL_ERROR;
|
|
}
|
|
arg0 = atoi(argv[1]);
|
|
result = fact(arg0);
|
|
sprintf(interp->result,"%d",result);
|
|
return TCL_OK;
|
|
}
|
|
</pre>
|
|
</tt>
|
|
</blockquote>
|
|
|
|
In addition to writing the wrapper function, a user will also need to
|
|
write code to add this function to the Tcl interpreter. In the case of
|
|
Tcl 7.5, this could be done by writing an initialization function to
|
|
be called when the extension is loaded dynamically. While writing
|
|
a wrapper function usually is not too difficult, the process quickly
|
|
becomes tedious and error prone as the number of functions increases.
|
|
Therefore, automated approaches for producing wrapper functions are
|
|
appealing--especially when working with a large number of C functions
|
|
or with C++ (in which case the wrapper code tends to get more complicated).
|
|
|
|
<h2> 3. Prior Work </h2>
|
|
|
|
The idea of automatically generating wrapper code is certainly not new.
|
|
Some efforts such as
|
|
Itcl++, Object Tcl, or the XS language included with Perl5, provide a mechanism for
|
|
generating wrapper code, but require the user to provide detailed
|
|
specifications, type conversion rules, or use a specialized
|
|
syntax [heidrich,Wetherall,perl5]. Large packages
|
|
such as the Visualization Toolkit (vtk) may use their own C/C++ translators,
|
|
but these almost always tend to be somewhat special purpose (in fact, SWIG started
|
|
out in this manner) [vtk].
|
|
If supporting multiple languages is the ultimate goal, a programmer might
|
|
consider a package such as ILU [Janssen].
|
|
Unfortunately,
|
|
this requires the user to provide specifications in IDL--a process which
|
|
is unappealing to many users.
|
|
SWIG is not necessarily intended to compete with
|
|
these approaches, but rather is designed to be a no-nonsense tool that
|
|
scientists and engineers can use to easily add Tcl and other scripting
|
|
languages to their own applications. SWIG is also
|
|
very different than Embedded Tk (ET) which also aims to
|
|
simplify code development [ET]. Unlike ET, SWIG is designed to
|
|
integrate C functions into Tcl/Tk as opposed to integrating Tcl/Tk into
|
|
C programs.
|
|
|
|
<h2> 4. A Quick Tour of SWIG </h2>
|
|
|
|
<h3> 4.1 Organization </h3>
|
|
|
|
<center>
|
|
<img alt = "SWIG Organization" src="fig1.gif"> <br>
|
|
Figure 1 : SWIG Organization <br> <br>
|
|
</center>
|
|
|
|
Figure 1 shows the structure of SWIG. At the core is a YACC parser
|
|
for reading input files along with some utility functions.
|
|
To generate code,
|
|
the parser calls about a dozen functions from a generic language
|
|
class to do things like write a wrapper function, link a
|
|
variable, wrap a C++ member function, etc... Each target language is
|
|
implemented as a C++ class containing the functions that emit
|
|
the resulting C code. If an ``empty'' language definition is given
|
|
to SWIG, it will produce no output. Thus, each language class can
|
|
be implemented in almost any manner.
|
|
The documentation system is implemented in a similar manner and can
|
|
currently produce ASCII, LaTeX, or HTML output. As output,
|
|
SWIG produces a C file that should be compiled and linked with the rest
|
|
of the code and a documentation file that can be used for later reference.
|
|
|
|
<h3> 4.2. Interface Files </h3>
|
|
|
|
As input, SWIG takes a single input file referred to as an ``interface file.''
|
|
This file contains a few SWIG specific directives, but otherwise contains
|
|
ANSI C function and variable declarations. Unlike the approach in [Heidrich], no type conversion rules are needed and all declarations are made
|
|
using familiar ANSI C/C++ prototypes. The following code shows an
|
|
interface file for wrapping a few C file I/O and memory management functions.
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
/* File : file.i */
|
|
%module fileio
|
|
%{
|
|
#include <stdio.h>
|
|
%}
|
|
|
|
FILE *fopen(char *filename, char *type);
|
|
int fclose(FILE *stream);
|
|
typedef unsigned int size_t
|
|
size_t fread(void *ptr, size_t size,
|
|
size_t nobj, FILE *stream);
|
|
size_t fwrite(void *ptr, size_t size,
|
|
size_t nobj,FILE *stream);
|
|
void *malloc(size_t nbytes);
|
|
void free(void *);
|
|
</pre> </tt> </blockquote>
|
|
|
|
The <tt> %module </tt> directive
|
|
sets the name of the initialization function. This is optional, but is
|
|
recommended if building a Tcl 7.5 module.
|
|
Everything inside the <tt> %{, %} </tt>
|
|
block is copied directly into the output, allowing the inclusion of
|
|
header files and additional C code.
|
|
Afterwards, C/C++ function and variable declarations are listed in any
|
|
order.
|
|
Building a new Tcl module is usually as
|
|
easy as the following :
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
unix > swig -tcl file.i
|
|
unix > gcc file_wrap.c -I/usr/local/include
|
|
unix > ld -shared file_wrap.o -o Fileio.so
|
|
</pre> </tt>
|
|
</blockquote>
|
|
|
|
|
|
<h3> 4.3. A Tcl Example </h3>
|
|
|
|
Newly added functions work like ordinary Tcl procedures. For
|
|
example, the following Tcl script copies a file using the binary
|
|
file I/O functions added in the previous example :
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
proc filecopy {name1 name2} {
|
|
set buffer [malloc 8192];
|
|
set f1 [fopen $name1 r];
|
|
set f2 [fopen $name2 w];
|
|
set nbytes [fread $buffer 1 8192 $f1];
|
|
while {$nbytes > 0} {
|
|
fwrite $buffer 1 $nbytes $f2;
|
|
set nbytes [fread $buffer 1 8192 $f1];
|
|
}
|
|
fclose $f1;
|
|
fclose $f2;
|
|
free $buffer
|
|
}
|
|
</pre> </tt>
|
|
</blockquote>
|
|
|
|
<h3> 4.4. Datatypes and Pointers </h3>
|
|
|
|
SWIG supports the basic datatypes of <tt> int, short,
|
|
long, float, double, char, </tt> and <tt> void </tt>
|
|
as well as signed and unsigned integers. SWIG
|
|
also allows derived types such as pointers, structures, and classes,
|
|
but these are all encoded as pointers.
|
|
If an unknown type is encountered, SWIG assumes
|
|
that it is a complex datatype that has been defined earlier.
|
|
No attempt is made to figure out what data that datatype actually contains
|
|
or how it should be used. Of course, this this is only possible
|
|
since SWIG's mapping of complex types into pointers allows them
|
|
to be handled in a uniform manner.
|
|
As a result, SWIG does not normally need any sort
|
|
of type-mapping, but <tt> typedef </tt> can be used to map any of the built-in
|
|
datatypes to new types if desired. <br> <br>
|
|
|
|
SWIG encodes pointers as hexadecimal strings with type-information. This
|
|
type information is used to provide a run-time type checking mechanism.
|
|
Thus, a typical SWIG pointer looks something like the following : <br> <br>
|
|
|
|
<center>
|
|
<tt> _1008e614_Vector_p </tt> <br> <br>
|
|
</center>
|
|
|
|
If this pointer is passed into a function requiring some other kind of
|
|
pointer, SWIG will generate a Tcl error and return an error message.
|
|
The NULL pointer is represented by the string "NULL". The SWIG run-time
|
|
type checker is saavy to typedefs and the relationship between base classes
|
|
and derived classes in C++. Thus if a
|
|
user specifies <br> <br>
|
|
|
|
<center>
|
|
<tt> typedef double Real; </tt> <br> <br>
|
|
</center>
|
|
|
|
the type checker knows that <tt> Real * </tt> and <tt> double * </tt>
|
|
are equivalent (more on C++ in a minute).
|
|
From the point of view of other Tcl extensions,
|
|
SWIG pointers should be seen as special "handles" except that they
|
|
happen to contain the pointer value and its type. <br> <br>
|
|
|
|
To some, this approach may seem horribly restrictive (or error prone),
|
|
but keep in mind that SWIG was primarily designed to work with
|
|
existing C applications. Since most C programs pass complex datatypes
|
|
around by reference this technique works remarkably well in practice.
|
|
Run time type-checking also eliminates most common crashes by catching
|
|
stupid mistakes such as using a wrong variable name or forgetting the
|
|
"$" character in a Tcl script. While it is still possible to crash Tcl by forging a
|
|
SWIG pointer value (or making a call to buggy C code), it is worth
|
|
emphasizing that existing Tcl extensions
|
|
may also crash if given an invalid handle.
|
|
|
|
<h3> 4.5. Global Variables and Constants </h3>
|
|
|
|
SWIG can install global C variables and constants using Tcl's variable linkage
|
|
mechanism.
|
|
Variables may also be declared as ``read only''
|
|
within the Tcl interpreter. The following example shows
|
|
how variables and constants can be added to Tcl :
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
// SWIG file with variables and constants
|
|
%{
|
|
%}
|
|
|
|
// Some global variables
|
|
extern int My_variable;
|
|
extern char *default_path;
|
|
extern double My_double;
|
|
|
|
// Some constants
|
|
#define PI 3.14159265359
|
|
#define PI_4 PI/4.0
|
|
enum colors {red,blue,green};
|
|
const int SIZEOF_VECTOR = sizeof(Vector);
|
|
|
|
// A read only variable
|
|
%readonly
|
|
extern int Status;
|
|
%readwrite
|
|
|
|
</pre>
|
|
</tt>
|
|
</blockquote>
|
|
|
|
<h3> 4.6. C++ Support </h3>
|
|
|
|
The SWIG parser can handle simple C++ class definitions and supports public
|
|
inheritance, virtual functions, static functions, constructors and
|
|
destructors. Currently, C++ translation is performed
|
|
by politely tranforming C++ code into C code and generating wrappers for the
|
|
C functions. For example, consider the following SWIG interface file
|
|
containing a C++ class definition:
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
%module tree
|
|
%{
|
|
#include "tree.h"
|
|
%}
|
|
|
|
class Tree {
|
|
public:
|
|
Tree();
|
|
~Tree();
|
|
void insert(char *item);
|
|
int search(char *item);
|
|
int remove(char *item);
|
|
static void print(Tree *t);
|
|
};
|
|
</pre>
|
|
</tt>
|
|
</blockquote>
|
|
|
|
When translated, the class will be access used the following set of
|
|
functions (created automatically by SWIG).
|
|
|
|
<blockquote>
|
|
<tt>
|
|
<pre>
|
|
Tree *new_Tree();
|
|
void delete_Tree(Tree *this);
|
|
void Tree_insert(Tree *this, char *item);
|
|
int Tree_search(Tree *this, char *item);
|
|
int Tree_remove(Tree *this, char *item);
|
|
void Tree_print(Tree *t);
|
|
</pre>
|
|
</tt>
|
|
</blockquote>
|
|
|
|
|
|
All C++ functions wrapped by SWIG explicitly require the <tt> this </tt> pointer
|
|
as shown. This approach has the advantage of working for all of the target
|
|
languages. It also makes it easier to pass objects between
|
|
other C++ functions since every C++ object is simply represented as a SWIG pointer. SWIG does not support function overloading, but overloaded functions
|
|
can be resolved by renaming them with the SWIG <tt> %name </tt> directive as follows:
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
class List {
|
|
public:
|
|
List();
|
|
%name(ListMax) List(int maxsize);
|
|
...
|
|
};
|
|
</pre> </tt> </blockquote>
|
|
|
|
The approach used by SWIG is
|
|
quite different than that used in systems such as
|
|
Object Tcl or vtk [vtk,Wetherall]. As a result, users of those systems
|
|
may find it to be confusing. However,
|
|
It is important to note that the modular design of
|
|
SWIG allows the user to completely redefine the output behavior of
|
|
the system. Thus, while the current C++ implementation is quite different
|
|
than other systems supporting C++, it would be entirely possible
|
|
write a new SWIG module that wrapped C++ classes into a representation
|
|
similar to that used by Object Tcl (in fact, in might even be possible
|
|
to use SWIG to produce the input files used for Object Tcl).
|
|
|
|
<h3> 4.7. Multiple Files and Code Reuse </h3>
|
|
|
|
An essential feature of SWIG is its support for multiple files
|
|
and modules. A SWIG interface file may include another interface
|
|
file using the "<tt> %include </tt>" directive. Thus, an interface
|
|
for a large system might be broken up into a collection of smaller
|
|
modules as shown
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
%module package
|
|
%{
|
|
#include "package.h"
|
|
%}
|
|
|
|
%include geometry.i
|
|
%include memory.i
|
|
%include network.i
|
|
%include graphics.i
|
|
%include physics.i
|
|
|
|
%include wish.i
|
|
</pre> </tt> </blockquote>
|
|
|
|
Common operations can be placed into a SWIG library for use in
|
|
all applications. For example, the <tt> %include wish.i </tt> directive
|
|
tells SWIG to include code for the <tt> Tcl_AppInit() </tt> function
|
|
needed to rebuild the <tt> wish </tt> program. The library can also be
|
|
used to build modules
|
|
allowing SWIG to be used with common Tcl extensions such
|
|
as Expect [Expect]. Of course, the primary use of the library is with
|
|
large applications such as Open-Inventor which contain hundreds of
|
|
modules and a substantial class hierarchy [Invent]. In this case a user
|
|
could use SWIG's include mechanism to selectively pick which modules
|
|
they wanted to use for a particular problem.
|
|
|
|
<h3> 4.8. The Documentation System </h3>
|
|
|
|
SWIG produces documentation in ASCII, LaTeX, or HTML
|
|
format describing everything that was wrapped. The documentation
|
|
follows the syntax rules of the target language and can
|
|
can be further enhanced by adding descriptions in a C/C++ comment
|
|
immediately following a declaration. These comments may also contain
|
|
embedded LaTeX or HTML commands. For example:
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
extern size_t fread(void *ptr, size_t size,
|
|
size_t nobj, FILE *stream);
|
|
/* {\tt fread} reads from {\tt stream} into
|
|
the array {\tt ptr} at most {\tt nobj} objects
|
|
of size {\tt size}. {\tt fread} returns
|
|
the number of objects read. */
|
|
</pre> </tt> </blockquote>
|
|
|
|
When output by SWIG and processed by LaTeX, this appears as follows :
|
|
|
|
<ul>
|
|
<tt> size_t : fread ptr size nobj stream </tt> <br>
|
|
<ul>
|
|
<tt> fread </tt> reads from <tt> stream </tt> into the array <tt> ptr </tt> at
|
|
most <tt> nobj </tt> objects of size <tt> size </tt>. <tt> fread </tt>
|
|
returns
|
|
the number of objects read.
|
|
</ul>
|
|
</ul>
|
|
|
|
<h3> 4.9. Extending the SWIG System </h3>
|
|
|
|
Finally, SWIG itself can be extended by the user to provide new
|
|
functionality. This is done by modifying an existing or creating a
|
|
new language class. A typical class must specify the following
|
|
functions that determine the behavior of the parser output :
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
// File : swigtcl.h
|
|
class TCL : public Language {
|
|
private:
|
|
// Put private stuff here
|
|
public :
|
|
TCL();
|
|
int main(int, char *argv[]);
|
|
void create_function(char *,char *,DataType*,
|
|
ParmList *);
|
|
void link_variable(char *,char *,DataType *);
|
|
void declare_const(char *,int,char *);
|
|
void initialize(void);
|
|
void headers(void);
|
|
void close(void);
|
|
void usage_var(char *,DataType*,char **);
|
|
void usage_func(char *,DataType*,ParmList*,
|
|
char **);
|
|
void usage_const(char *,int,char*,char**);
|
|
void set_module(char *);
|
|
void set_init(char *);
|
|
};
|
|
</pre> </tt> </blockquote>
|
|
|
|
Descriptions of these functions can be found in the SWIG
|
|
users manual. To build a new version of SWIG, the user
|
|
only needs to provide the function definitions and a main
|
|
program which looks something like the following :
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
|
|
// SWIG main program
|
|
|
|
#include "swig.h"
|
|
#include "swigtcl.h"
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
Language *lang;
|
|
|
|
lang = new TCL;
|
|
SWIG_main(argc,argv,lang,(Documentation *) 0);
|
|
|
|
}
|
|
</pre> </tt> </blockquote>
|
|
|
|
When linked with a library file, any extensions and
|
|
modifications can now be used with the SWIG parser. While
|
|
writing a new language definition is not entirely trivial,
|
|
it can usually be done by just copying one of the existing
|
|
modules and modifying it appropriately.
|
|
|
|
<h2> 5. Examples </h2>
|
|
|
|
<h3> 5.1. A C-Tcl Linked List </h3>
|
|
|
|
SWIG can be used to build simple data structures that are usuable in
|
|
both C and Tcl. The following code shows a SWIG interface file for
|
|
building a simple linked list.
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
/* File : list.i */
|
|
%{
|
|
struct Node {
|
|
Node(char *n) {
|
|
name = new char[strlen(n)+1];
|
|
strcpy(name,n);
|
|
next = 0;
|
|
};
|
|
char *name;
|
|
Node *next;
|
|
};
|
|
%}
|
|
|
|
// Just add struct definition to
|
|
// the interface file.
|
|
|
|
struct Node {
|
|
Node(char *);
|
|
char *name;
|
|
Node *next;
|
|
};
|
|
|
|
</pre> </tt> </blockquote>
|
|
|
|
When used in a Tcl script, we can now create new nodes and access
|
|
individual members of the <tt> Node </tt> structure. In fact, we can
|
|
write code to convert between Tcl lists and linked lists entirely
|
|
in Tcl as shown :
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
# Builds linked list from a Tcl list
|
|
proc buildlist {list head} {
|
|
set nitems [llength $list];
|
|
for {set i 0} {$i < $nitems} {incr i -1} {
|
|
set item [lrange $list $i $i]
|
|
set n [new_Node $item]
|
|
Node_set_next $n $head
|
|
set head $n
|
|
}
|
|
return $head
|
|
}
|
|
# Builds a Tcl list from a linked list
|
|
proc get_list {llist} {
|
|
set list {}
|
|
while {$llist != "NULL"} {
|
|
lappend list [Node_name_get $llist]
|
|
set llist [Node_get_next $llist]
|
|
}
|
|
return $list
|
|
}
|
|
</pre> </tt> </blockquote>
|
|
|
|
When run interactively, we could now use our Tcl functions as
|
|
follows.
|
|
|
|
<blockquote>
|
|
<tt> <pre>
|
|
% set l {John Anne Mary Jim}
|
|
John Anne Mary Jim
|
|
% set ll [buildlist $l _0_Node_p]
|
|
_1000cab8_Node_p
|
|
% get_list $ll
|
|
Jim Mary Anne John
|
|
% set ll [buildlist {Mike Peter Dave} $ll]
|
|
_1000cc38_Node_p
|
|
% get_list $ll
|
|
Dave Peter Mike Jim Mary Anne John
|
|
%
|
|
</pre> </tt> </blockquote>
|
|
Aside from the pointer values, our script acts like any other
|
|
Tcl script. However, we have built up a real
|
|
C data structure that could be easily passed to other C functions
|
|
if needed.
|
|
|
|
<h3> 5.2. Using C Data-Structures with Tk </h3>
|
|
|
|
In manner similar to the linked list example, Tcl/Tk can be used
|
|
to build complex C/C++ data structures. For example, suppose we
|
|
wanted to interactively build a graph of ``Nodes'' for use in a C
|
|
application.
|
|
A typical interface file might include the following functions:
|
|
|
|
<blockquote> <tt> <pre>
|
|
%{
|
|
#include "nodes.h"
|
|
%}
|
|
|
|
%include wish
|
|
extern Node *new_node();
|
|
extern void AddEdge(Node *n1, Node *n2);
|
|
</pre> </tt> </blockquote>
|
|
|
|
Within a Tcl/Tk script, loosely based on one to make ball and stick graphs
|
|
in [Ousterhout], a graph could be built as follows:
|
|
|
|
<blockquote> <tt> <pre>
|
|
proc makeNode {x y} {
|
|
global nodeX nodeY nodeP edgeFirst edgeSecond
|
|
set new [.create oval [expr $x-15] \
|
|
[expr $y-15] [expr $x+15] \
|
|
[expr $y+15] -outline black \
|
|
-fill white -tags node]
|
|
set newnode [new_node]
|
|
set nodeX($new) $x
|
|
set nodeY($new) $y
|
|
set nodeP($new) $newnode
|
|
set edgeFirst($new) {}
|
|
set edgeSecond($new) {}
|
|
}
|
|
proc makeEdge {first second} {
|
|
global nodeX nodeY nodeP edgeFirst edgeSecond
|
|
set x1 $nodeX($first); set y1 $nodeY($first)
|
|
set x2 $nodeX($second); set y2 $nodeY($second)
|
|
set edge [.c create line $x1 $y1 $x2 $y2 \
|
|
-tags edge}
|
|
.c lower edge
|
|
lappend edgeFirst($first) $edge
|
|
lappend edgeSecond($first) $edge
|
|
AddEdge $nodeP($first) $nodeP($second)
|
|
}
|
|
</pre> </tt> </blockquote>
|
|
|
|
These functions create
|
|
Tk canvas items, but also attach a pointer to a C data structure to each one.
|
|
This is done by maintaining an associative array mapping
|
|
item identifiers to pointers (with the <tt> nodeP() </tt> array).
|
|
When a
|
|
particular ``node'' is referenced later, we can use this to
|
|
get its pointer use it in calls to C functions.
|
|
|
|
<h3> 5.3. Parsing a C++ Simple Class Hierarchy </h3>
|
|
|
|
As mentioned earlier, SWIG can handle C++ classes and public
|
|
inheritance. The following example provides a few classes
|
|
and illustrates how this is accomplished (some code has been
|
|
ommitted for readability).
|
|
|
|
<blockquote> <tt> <pre>
|
|
// A SWIG inheritance example
|
|
%module shapes
|
|
%{
|
|
#include "shapes.h"
|
|
%}
|
|
|
|
class Shape {
|
|
private:
|
|
double xc, yc;
|
|
public:
|
|
virtual double area() = 0;
|
|
virtual double perimeter() = 0;
|
|
void set_position(double x, double y);
|
|
void print_position();
|
|
};
|
|
|
|
class Circle: public Shape {
|
|
private:
|
|
double radius;
|
|
public:
|
|
Circle(double r);
|
|
double area();
|
|
double perimeter();
|
|
};
|
|
|
|
class Square : public Shape {
|
|
private:
|
|
double width;
|
|
public:
|
|
Square(double w);
|
|
double area();
|
|
double perimeter();
|
|
};
|
|
|
|
</pre> </tt> </blockquote>
|
|
|
|
Now, when wrapped by SWIG (note :
|
|
When parsing C++ classes, SWIG throws away everything declared as private,
|
|
inline code, and alot of the other clutter found in C++ header files.
|
|
Primarily this is provided only to make it easier to build interfaces from
|
|
existing C++ header files.), we can use our class structure as follows:
|
|
|
|
<blockquote> <tt> <pre>
|
|
% set c [new_Circle 4]
|
|
_1000ad70_Circle_p
|
|
% set s [new_Square 10]
|
|
_1000adc0_Square_p
|
|
% Shape_area $c
|
|
50.26548246400000200
|
|
% Shape_area $s
|
|
100.00000000000000000
|
|
% Shape_set_position $c -5 10
|
|
% Circle_print_position $c
|
|
xc = -5, yc = 10
|
|
%
|
|
</pre> </tt> </blockquote>
|
|
|
|
In our example, we have created new <tt> Circle </tt> and <tt> Square </tt> objects,
|
|
but these can be used interchangably in any functions defined in the <tt> Shape </tt>
|
|
base class. The SWIG type checker is encoded with the class hierarchy and
|
|
knows the relationship between the different classes. Thus, while an
|
|
object of type <tt> Circle </tt> is perfectly acceptable to a function operating
|
|
on shapes, it would be unacceptable to a function operating only on the
|
|
<tt> Square </tt> type. As in C++, any functions in the base class can be
|
|
called in the derived class as shown by the <tt> Circle_print_position </tt>
|
|
function above.
|
|
|
|
<h2> 6. Using SWIG in Real Applications </h2>
|
|
|
|
So far only a few simple toy examples have been presented to illustrate
|
|
the operation of SWIG in general.
|
|
This section will describe how SWIG can be used
|
|
with larger applications.
|
|
|
|
<h3> 6.1. Use in Scientific Applications </h3>
|
|
|
|
Many users, especially within the scientific and engineering
|
|
community, have spent years developing simulation codes.
|
|
While many of these users appreciate the
|
|
power that a scripting language can provide, they don't want to
|
|
completely rewrite their applications or spend all of their time
|
|
trying to build a user-interface (most users would rather be working
|
|
on the scientific problem at hand). While SWIG certainly won't do everything,
|
|
it can dramatically simplify this process. <br> <br>
|
|
|
|
As an example, the first SWIG application was the SPaSM molecular
|
|
dynamics code developed at Los Alamos National Laboratory
|
|
[Beazley,Lomdahl]. This code is currently being used for materials
|
|
science problems and consists of more than 200 C functions and about 20000 lines
|
|
of code.
|
|
In order to use SWIG, only the <tt> main() </tt> function had to be
|
|
rewritten along with a few other minor modifications.
|
|
The full user interface is built using a collection of modules for
|
|
simulation, graphics, memory management, etc...
|
|
A user may also supply their own interface modules--allowing the code to
|
|
be easily extended with new functionality capabilities as needed.
|
|
All of the interface files, containing a few hundred lines of
|
|
function declarations, are automatically translated into more than
|
|
2000 lines of wrapper code at compile time. As a result, many users
|
|
of the system know how to extend it, but are unaware of the actual
|
|
wrapping procedure. <br> <br>
|
|
|
|
After modifying the SPaSM code to use SWIG, most of the C code remained
|
|
unchanged. In fact, in subsequent work, we were able to eliminate
|
|
more than 25% of the source code--almost all of which was related to the
|
|
now obsolete interface method that we replaced. More importantly, we
|
|
were able to turn a code that was difficult to use and modify into a
|
|
flexible package that was easy to use and extend. This has had a
|
|
huge impact, which can not be understated, on the use of the SPaSM
|
|
code to solve real problems.
|
|
|
|
<h3> 6.2. Open-GL and Inventor </h3>
|
|
|
|
While SWIG was primarily designed to work with application codes, it
|
|
can also be used to wrap large libraries. At the University of Utah,
|
|
Peter-Pike Sloan used SWIG to wrap the entire contents of the Open-GL
|
|
library into Tcl for use with an Open-GL Tk widget. The process of
|
|
wrapping Open-GL with SWIG was as simple as the following :
|
|
|
|
<ul>
|
|
<li> Make a copy of the <tt> gl.h </tt> header file.
|
|
<li> Clean it up by taking a few C preprocessor directives out
|
|
of it. Fix a few typedefs.
|
|
<li> Insert the following code into the beginning
|
|
<blockquote> <tt> <pre>
|
|
%module opengl
|
|
%{
|
|
#include <GL/gl.h>
|
|
%}
|
|
... Copy edited gl.h here ...
|
|
</pre> </tt> </blockquote>
|
|
|
|
<li> Modify the Open-GL widget to call <tt> Opengl_Init </tt>.
|
|
<li> Recompile
|
|
</ul>
|
|
|
|
The entire process required only about 10 minutes of work, but resulted
|
|
in more than 500 constants and 360 functions being added to Tcl. Furthermore,
|
|
this extension allows Open-GL commands to be issued directly from Tcl's
|
|
interpreted environment as shown in this example
|
|
(from the Open-GL manual [OpenGL]).
|
|
|
|
<blockquote> <tt> <pre>
|
|
% ... open GL widget here ...
|
|
% glClearColor 0.0 0.0 0.0 0.0
|
|
% glClear $GL_COLOR_BUFFER_BIT
|
|
% glColor3f 1.0 1.0 1.0
|
|
% glOrtho -1.0 1.0 -1.0 1.0 -1.0 1.0
|
|
% glBegin $GL_POLYGON
|
|
% glVertex2f -0.5 -0.5
|
|
% glVertex2f -0.5 0.5
|
|
% glVertex2f 0.5 0.5
|
|
% glVertex2f 0.5 -0.5
|
|
% glEnd
|
|
% glFlush
|
|
</pre> </tt> </blockquote>
|
|
|
|
Early work has also been performed on using SWIG to wrap portions
|
|
of the Open-Inventor package [Invent]. This is a more ambitious project
|
|
since Open-Inventor consists of a very large collection of header
|
|
files and C++ classes. However, the SWIG library mechanism can be
|
|
used effective in this case. For each Inventor header, we can create
|
|
a sanitized SWIG interface file (removing alot of the clutter found in
|
|
the header files). These interface files can then be organized to mirror the
|
|
structure of the Inventor system. To build a module for Tcl, a user
|
|
could simply specify which modules they wanted to use as follows :
|
|
|
|
<blockquote> <tt> <pre>
|
|
%module invent
|
|
%{
|
|
... put headers here ...
|
|
%}
|
|
|
|
%include "Inventor/Xt/SoXt.i"
|
|
%include "Inventor/Xt/SoXtRenderArea.i"
|
|
%include "Inventor/nodes/SoCone.i"
|
|
%include "Inventor/nodes/SoDirectionalLight.i"
|
|
%include "Inventor/nodes/SoMaterial.i"
|
|
%include "Inventor/nodes/SoPerspectiveCamera.i"
|
|
%include "Inventor/nodes/SoSeparator.i"
|
|
</pre> </tt> </blockquote>
|
|
|
|
While wrapping the Inventor library will require significantly
|
|
more work than Open-GL, SWIG can be used effectively with such
|
|
systems.
|
|
|
|
<h3> 6.3. Building Interesting Applications by Cut and Paste </h3>
|
|
|
|
SWIG can be used to construct tools out of dissimiliar
|
|
packages and libraries.
|
|
In contrast to combining packages at an input level as one might
|
|
do with with Expect, SWIG can be used to build applications at a
|
|
function level [Expect].
|
|
For example, using
|
|
a simple interface file, you can wrap the
|
|
C API of MATLAB 4.2 [MATLAB].
|
|
This in turn lets you build a Tcl/Tk interface for controlling
|
|
MATLAB programs. Separately, you could wrap a numerical simulation
|
|
code. Now, a few translation functions could be written and both
|
|
packages combined into a single Tcl/Tk application. You have
|
|
now built a simple ``computational steering'' system that allows you
|
|
to run a simulation code, visualize data in MATLAB, and control the
|
|
entire system with a Tcl/Tk based interface. Figure 2 shows
|
|
a screen snapshot from such a simulation in which the SPaSM molecular
|
|
dynamics code, a visualization module library, image server (using xv),
|
|
and MATLAB have been
|
|
combined. Under this system, the user can interactively set up
|
|
and run simulations while watching the results evolve in real-time.
|
|
<br> <br>
|
|
<center>
|
|
<img alt = "Tcl/Tk/MATLAB/SPASM Figure" src="steer2.gif"> <br>
|
|
Figure 2: Simple Tcl/Tk Steering Application Built with SWIG <br>
|
|
</center>
|
|
|
|
<h3> 6.4. Language Independent Applications </h3>
|
|
|
|
|
|
Each scripting language has it's own strengths and weaknesses.
|
|
Rather
|
|
than trying to choose the language that is the best at everything (which
|
|
is probably impossible),
|
|
SWIG allows a user to use the best language for the job at hand. I'm
|
|
always finding myself writing little utilities and programs to support
|
|
my scientific computing work. Why should I be forced to use only one
|
|
language for everything? With SWIG, I can put a Perl interface on a
|
|
tool and switch over to Tcl/Tk at anytime by just changing a few
|
|
Makefile options. <br> <br>
|
|
|
|
Since SWIG provides a language-independent interface specification,
|
|
it is relatively easy to use SWIG generated modules in a variety of
|
|
interesting applications. For example, the identical MATLAB
|
|
module used in the last Tcl example could be imported as Perl5 module
|
|
and combined with a Perl script to produce graphical displays from
|
|
Web-server logs as shown in Figure 3. <br> <br>
|
|
|
|
<center>
|
|
<img alt="Perl5 web stats example" src="hits.gif"> <br>
|
|
Figure 3:Web-server activity graph. Generated from a Perl script, but
|
|
uses an unmodified SWIG generated module for integrating MATLAB with Tcl/Tk. <br> <br>
|
|
</center>
|
|
|
|
Some have promoted the idea of encapsulating several languages into
|
|
a higher level language as has been proposed with Guile [Lord].
|
|
This may be fine if one wants to write code that uses different languages
|
|
all at once, but when I want to use only Tcl or Perl for an application,
|
|
I almost always prefer using the real versions instead of an encapsulated
|
|
variant. SWIG makes this possible without much effort.
|
|
|
|
<h3> 6.5. Using Tcl as a debugger </h3>
|
|
|
|
Since SWIG can work with existing C code, it can be used quite effectively
|
|
as a debugger.
|
|
You can wrap C functions and interact
|
|
with them from <tt> tclsh </tt> or <tt> wish </tt>.
|
|
Unlike most traditional debuggers, you
|
|
can create objects, buffers, arrays, and other things on the fly
|
|
by putting appropriate definitions in the interface file.
|
|
Since <tt> tclsh </tt> or <tt> wish </tt> provides a <tt> main() </tt> function, you
|
|
can even rip small pieces out of a larger package without including
|
|
that package's main program.
|
|
Scripts can be written to test out different parts of your
|
|
code as it is developed. When you are satisfied with the module,
|
|
removing the interface is as easy as discarding the SWIG interface
|
|
file. I have used SWIG as a debugger for developing graphics libraries,
|
|
network servers, simulation codes, and a wide range of other projects--some
|
|
of which didn't even use a scripting language when completed.
|
|
|
|
<h2> 7. Limitations </h2>
|
|
|
|
SWIG represents a balance of flexibility and ease of use.
|
|
I have always felt that the tool shouldn't be more complicated than
|
|
the original problem. Therefore, SWIG certainly won't do everything.
|
|
While I believe the pointer representation of complex datatypes works
|
|
well in practice, some users have found this to be too general or
|
|
confusing. SWIG's
|
|
support for C++ is also limited by the fact that SWIG does not support
|
|
operator overloading, multiple inheritance, templates, and a number
|
|
of other more advanced C++ features. SWIG also lacks support for
|
|
default or variable length function arguments, array notation,
|
|
pointers to functions (unless hidden by a typedef) and an exception
|
|
mechanism.
|
|
Finally, SWIG
|
|
does
|
|
not support all of the features of Tcl such as associative arrays.
|
|
These aren't an inherent part of the C language so it is difficult
|
|
to generalize how they should be handled or generated. I prefer to
|
|
keep the tool simple while leaving these issues up to the individual
|
|
programmer. <br> <br>
|
|
|
|
|
|
Fortunately, it is almost always possible to work around
|
|
many problems by writing special library functions or making
|
|
modifications to the SWIG language modules to produce the desired effect.
|
|
For example, when wrapping Open-GL, SWIG installed all of the Open-GL
|
|
constants as Tcl global variables. Unfortunately, none of these
|
|
variables were accessible inside Tcl procedures unless they were explicitly
|
|
declared global. By making a modification to the Tcl language module,
|
|
it was possible to put the GL constants into hash table and perform
|
|
a hidden ``lookup'' inside all of the GL-related functions. Similarly,
|
|
much of the functionality of SWIG can be placed into library
|
|
files for use in other modules.
|
|
|
|
<h2> 8. Conclusions </h2>
|
|
|
|
SWIG has been in use for approximately one year. At Los Alamos National Laboratory, its use with the SPaSM code has
|
|
proven to
|
|
be a remarkably simple, stable, and bug-free way to build and modify user
|
|
interfaces. At the University of Utah, SWIG is being used in a variety
|
|
of other applications where the users have quickly come to appreciate its
|
|
ease of use and flexibility. <br> <br>
|
|
|
|
While SWIG may be inappropriate for certain applications, I feel that it
|
|
opens up Tcl/Tk, Python, Perl, Guile, and other languages to
|
|
users who would like to develop interesting user interfaces for their codes,
|
|
but who don't want to worry about the low-level details or figuring
|
|
out how to use a complicated tool. <br> <br>
|
|
|
|
Future directions for SWIG include support for other scripting languages,
|
|
and a library of extensions. SWIG may also be extended to support packages
|
|
such as incremental Tcl. Work is also underway to port SWIG to non-unix
|
|
platforms.
|
|
|
|
<h2> Acknowledgments </h2>
|
|
|
|
This project would not have been possible without the support of a number
|
|
of people. Peter Lomdahl, Shujia Zhou, Brad Holian, Tim Germann, and Niels
|
|
Jensen at Los Alamos National Laboratory were the first users and were
|
|
instrumental in testing out the first designs. Patrick Tullmann at
|
|
the University of Utah suggested the idea of automatic documentation
|
|
generation. John Schmidt, Kurtis
|
|
Bleeker, Peter-Pike Sloan, and Steve Parker at the University of Utah tested out some of the newer versions and
|
|
provided valuable feedback. John Buckman suggested many interesting
|
|
improvements and has been instrumental the recent development of SWIG.
|
|
Finally
|
|
I'd like to thank Chris Johnson and the members of the Scientific
|
|
Computing and Imaging group at the University of Utah for their continued
|
|
support and for putting up with my wrapping every software package
|
|
I could get my hands on. SWIG was developed in part under the
|
|
auspices of the US Department of Energy, the National Science Foundation,
|
|
and National Institutes of Health.
|
|
|
|
<h2> Availability </h2>
|
|
|
|
SWIG is free software and available via anonymous FTP at <br> <br>
|
|
|
|
<center>
|
|
<tt> ftp.cs.utah.edu/pub/beazley/SWIG </tt> <br> <br>
|
|
</center>
|
|
|
|
More information is also available on the SWIG homepage at <br> <br>
|
|
|
|
<center>
|
|
<a href="http://www.cs.utah.edu/~beazley/SWIG"> <tt> http://www.cs.utah.edu/~beazley/SWIG </tt> </a>
|
|
</center> <br> <br>
|
|
|
|
<h2> References </h2>
|
|
|
|
[Beazley] D.M. Beazley and P.S. Lomdahl,
|
|
<em> Message-Passing Multi-Cell Molecular Dynamics on the Connection
|
|
Machine 5 </em>, Parallel Computing 20 (1994) p. 173-195. <br> <br>
|
|
|
|
[ET] Embedded Tk, <br>
|
|
<tt> ftp://ftp.vnet.net/pub/users/drh/ET.html </tt> <br> <br>
|
|
|
|
[Expect] Don Libes, <em> Exploring Expect </em>, O'Reilly & Associates, Inc. (1995). <br> <br>
|
|
|
|
[Heidrich] Wolfgang Heidrich and Philipp Slusallek, <em>
|
|
Automatic Generation of Tcl Bindings for C and C++ Libraries.</em>,
|
|
USENIX 3rd Annual Tcl/Tk Workshop (1995). <br> <br>
|
|
|
|
[Janssen] Bill Janssen, Mike Spreitzer, <em> ILU : Inter-Language Unification via Object Modules </em>, OOPSLA 94 Workshop on Multi-Language
|
|
Object Models. <br> <br>
|
|
|
|
[Lomdahl] P.S. Lomdahl, P. Tamayo,
|
|
N.Gronbech-Jensen, and D.M. Beazley,
|
|
<em> Proc. of Supercomputing 93 </em>, IEEE Computer Society (1993), p. 520-527.
|
|
<br> <br>
|
|
|
|
[Lord] Thomas Lord, <em> An Anatomy of Guile, The Interface to
|
|
Tcl/Tk </em>, Proceedings of the USENIX 3rd Annual Tcl/Tk Workshop (1995). <br> <br>
|
|
|
|
[MATLAB] <em> MATLAB External Interface Guide </em>, The Math Works, Inc. (1993).
|
|
<br> <br>
|
|
|
|
[Ousterhout] John K. Ousterhout, <em> Tcl and the Tk Toolkit </em>, Addison-Wesley Publishers (1994). <br> <br>
|
|
|
|
[Perl5] Perl5 Programmers reference, <br>
|
|
<tt> http://www.metronet.com/perlinfo/doc </tt>, (1996). <br> <br>
|
|
|
|
[vtk] W. Schroeder, K. Martin, and B. Lorensen, <em> The
|
|
Visualization Toolkit </em>, Prentice Hall (1995). <br> <br>
|
|
|
|
[Invent] J. Wernecke,<em> The Inventor Mentor </em>, Addison-Wesley Publishing (1994). <br> <br>
|
|
|
|
[Wetherall] D. Wetherall, C. J. Lindblad, <em> Extending Tcl for
|
|
Dynamic Object-Oriented Programming </em>, Proceedings of the USENIX 3rd Annual Tcl/Tk Workshop (1995). <br> <br>
|
|
|
|
[OpenGL] J. Neider, T. Davis, M. Woo, <em> OpenGL Programming Guide </em>, Addison-Wesley Publishing (1993). <br> <br>
|
|
|
|
</body>
|
|
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|