Introduction
Rapid prototyping techniques can become ineffective when the prototyping is not actually rapid, or when it becomes too difficult. One of the obstacles to rapid prototyping is the "edit-compile-test (and possibly crash)" cycle of development.
You can avoid the compile portion through the use of interpreted code, written in languages such as Java, TCL/TK, Python, or Perl. Without the compile cycle, programs can go directly from editor to running program in a fraction of the time for typical C++ programs. As a plus, TCL/TK, Python, and Perl provide interactive sessions, letting you experiment with the commands available and test out new features immediately.
A major drawback in using scripting languages is the difficulty in adding capabilities to the languages through the use of libraries. Although the scripting languages themselves do provide mechanisms for incorporating library function calls, these mechanisms often have subtle nuances that have lead me to great frustration. The problems mount when I want to have the same functions available in two languages. The solution I've found is SWIG [1].
SWIG stands for Simplified Wrapper and Interface Generator, and its name describes the functionality of the program very well. For us lazy programmers, SWIG automates some of the tedium associated with incorporating routines from a library into a scripting language. In a nutshell, SWIG parses C and C++ header files, looking for #defined constants, global variables, classes, structures, enumerations, and function declarations. It uses the parsed information to generate the C or C++ code (called a wrapper) that interfaces the scripting language and the library calls represented by the header files.
As of this writing, SWIG works with:
- TCL 7.3, and all newer versions including TCL 8/0
- Python 1.3 and newer
- Perl 5.003 or newer, including ActiveWare Perl for Win32
- Perl 4.036
- FSF Guile 1.0
There is also alpha support for Java. SWIG runs on a number of Unix platforms, including Linux; Windows 95/NT; and Macintosh.
To demonstrate the use of SWIG, I'll first cover the general method of writing interface code, what SWIG does with the different components of a library, and finally, present a working example of how I've used SWIG to incorporate the Image Magick library of image processing routines into Python.
SWIG Basics
SWIG is used to generate a module for a particular scripting language. A module contains a collection of constants, global variables, structures, classes, and procedures that are wrapped up and used in the scripting language. SWIG generates wrapper code to allow the elements of the module to be accessed in a given scripting language. All the languages that SWIG operates with are able to import the module dynamically into the interpreter using shared languages. SWIG also generates rudimentary documentation on the elements that it wraps into the interpreter using shared libraries.
Constants and Global Variables
When SWIG encounters a #define statement that defines a constant, the constant appears in Python as a global read-only variable with the same name, and with the value that was defined in the header file. Thus,
#define OPENIMAGE 1
translates to the variable
modulename.OPENIMAGE
in Python. Global variables are handled in much the same way, except that they are not read only. SWIG/Python understands all the simple data types (char, int, float, etc...). And because SWIG process typemap statements (explained later), even more complex data types are available in the scripting language. The scripting language is able to use these variables just as it does any other native variable.
Classes and Structures
To understand the handling of classes [2], you must understand how SWIG handles data. To manage classes, SWIG refers to each object as a pointer. Suppose a C++ procedure takes an object (say foo) as a parameter. In Python, you simply pass a representation of a pointer. SWIG does the proper dereferencing and calls the procedure correctly.
SWIG uses different representations for different scripting languages. For Python, SWIG represents an object of type Bar as a character string of the form _10048ff0_Bar_p. The first part of the string represents the address of the object, _Bar_ represents the Bar object's class, and _p says it's a pointer. In general, you never have to work with the string representations, and you should never modify them.
SWIG performs run-time type checking as well, and will generate an error if you try to use an invalid class in a procedure. SWIG wraps only the public functions and members of a class. It is also quite capable of understanding virtual functions, static members, and superclasses.
SWIG also lets you be selective about what you include in the module, so only the elements that you want are included. SWIG currently does not wrap operators, overloaded functions, or friend functions. See the sidebar for a short example of how wrapped constants, procedures, and classes look in Python.
Wrapping the Image Magick Library
Image Magick (http://www.wizards.dupont.com/cristy/) is an image processing C library available for a wide variety of platforms. The chief advantage of Image Magick is its ability to read in many different kinds of images, operate on them, and write them out in a different format. I often write short Python scripts with the wrapped Image Magick libary to perform one-shot batch processing of images, or to simply do the processing interactively. Figure 1 shows an interface file for SWIG that describes the structures and functions of Image Magick that form the basis of the example.
To create an interface file for SWIG, I usually copy the header that I'm interested in, then rip out all the extra stuff. SWIG does tend to get confused with #define and #ifdef statements, so they are usually the first to go. I then edit the stripped down header to make it palatable to SWIG. After consulting the Image Magick documentation, I determined that the two structures to get started with are ImageInfo and Image.
At the top of Figure 1, a line appears starting with the directive %module. Each SWIG directive starts with a %; the %module directive tells SWIG to name this module "Image". The next section is a block of code contained in a %{ %} pair. SWIG copies all the code enclosed in this pair directly into the generated C code. This section is typically used to include header files. In this case, the only file needed is magick.h.
Typemaps
The next directive involves one of the most powerful and mysterious features of SWIG, typemaps. A typemap provides control over the conversion of scripting language elements to C/C++ elements. The first typemap directive provides for a character string of any length (defined by the SWIG keyword ANY) to be assigned to a C++ class member that is a character array, using a string copy. SWIG replaces $target with the name of the char[] member; $source is the string as represented in Python; and $dim0 is the length of the member.
In general, writing typemap directives requires a basic understanding of the scripting langage in use, and how SWIG operates. SWIG treats char * members as allocated memory, and will free the memory and reallocate it as needed when the member is accessed. SWIG makes fixed-length arrays read-only, but you can neatly circumvent this restriction by using this typemap.
Consider a class such as the following:
class Foo { public: char name[100]; int length; Foo() { name = '\0'; length = 10; }; Foo* Copy(); };
Because SWIG does not by default handle read/write arrays, you cannot write to the member name. If in Python foo represents an object of class Foo, you can get the value of foo->name by using foo.name, but you cannot write to name using foo.name = "NewValue". The char[ANY] typemap circumvents this problem simply by taking the Python object $source, a string, and using strncpy to place the value into the member name, represented by $target. In effect, the typemap performs the steps that you would have to go through in a strictly C++ interface. SWIG replaces $dim0 with the length of the array, 100 in member name for class Foo.
The next section of Figure 1 is the class definition for the ImageInfo structure. The only real member of importance is the filename member. Setting the filename to the name of an image causes the ReadImage procedure to read an image into memory and return a pointer to it.
Even though Image Magick is strictly a C library, we can simulate C++ functionality by using the %addmethods SWIG directive. %addmethods allows you to add constructors, destructors, and methods to a class regardless of what is in the C++ declaration, allowing easy extension. In the case of the ImageInfo structure, the constructor needs to allocate memory, and to call GetImageInfo to initialize it. The destructor simply calls DestroyImageInfo to release the memory. In any method added in an %addmethods block, self can be used like this in C++, so DestroyImageInfo takes self as a parameter, which is a pointer to the particular ImageInfo structure.
The Image structure needs a bit more work to be useful. The member filename is essential to the class, as it contains the name of a file to which the image is written. The columns, rows, depth, x_resolution, and y_resolution members are not so essential, but provide useful information about the image. Since I use the library only to read in files rather than construct them on the fly, I don't need to provide a way to construct an image. The ReadImage procedure takes care of allocating and initializing an Image instance. The Image class destructor simply calls DestroyImage.
Image Magick provides a great number of image processing routines. Many are useful, some are not so useful. I've added some of the simpler and more dramatic routines to the Image class. Notice that most of them return an Image *. A new Python instance of the Image class is created and returned for each of these, complete with all the object-oriented features, just as would be the case in C++.
The last part of the file is the ReadImage procedure. This procedure takes an ImageInfo structure as a parameter, and returns an Image * as a full Python object. ReadImage could be incorporated into the Image class through an %addmethods directive, but I found the interface to be cleaner with it outside the Image class.
An Example Build and Use
The wrapper code generate by SWIG is designed to be transparently compiled on Unix, Windows NT/95, and Macintosh. SWIG does provide platform-independent code, and I frequently generate wrapper code on Unix, and compile the code on Windows NT. The following snippet shows an example of compiling the sample code under RedHat 5.0 Linux (assuming a filename of sample.i):
Linux$ swig -python -shadow sample.i Generating wrappers for Python Linux$ cc -c sample_wrap.c -I/usr/local/include/python -I/usr/local/include/magick Linux$ ld -G -o Imagec.so sample_wrap.o -lMagick -lX11 -ljpeg -lmpeg -lpng -lz -lm
The shared library must be named Modulenamec.so, where Modulename is Image in the example and is set using the %module directive in the sample.i file. SWIG also generates a Modulename.py file, which imports Modulenamec.so and defines some Python classes that effectively wrap the shared library classes to make them appear as native Python objects.
Figure 2 is an excerpt from an interactive Python session, showing the fruits of wrapping the Image Magick library through SWIG. >>> is Python's interactive prompt, and ... is the continuation prompt.
Conclusion
I use SWIG extensively to create interfaces for libraries that I need to rapidly understand and use interactively. The Image Magick library is extremely useful for incorporating image processing into the different scripting languages, and I use it quite often to easily convert files from one format to another with a bit of processing in-between. Through the use SWIG, it is very easy to rapidly prototype applications that showcase different libraries. I often end up being able to use the scripting interface rather than re-writing the code in C/C++.
Notes
[1] SWIG is freely available at www.swig.org.
[2] Since SWIG treats them the same, I'll refer to both classes and structures as classes.
Daniel Blezek ([email protected]) is a Ph.D. student in Biomedical Engineering studying medical image processing, scientific and medical visualization, and virtual reality. Between feeding times for his baby daughter, he likes to tinker around learning new programming languages and exploring the wonders of Linux.