SWIG was originally designed to make it extremely easy for scientists and engineers to build flexible software without having to get a degree in software engineering. Because of this, the use of SWIG tends to be somewhat informal and ad-hoc (e.g., SWIG does not require users to provide formal interface specifications). Although this style of development isn't appropriate for every project, it is particularly well suited to software development in the small; especially research and development work as is commonly found in scientific and engineering projects.
Much of the problem with C is due to the way in which many programs are organized. For example, a lot of programs are structured as follows:
In this case, the main() program may read command line options or simple commands from stdin. However, modifying or extending the program to do something new requires changing the C code, recompiling, and testing. If you make a mistake, you need to repeat this cycle until things work. Of course, as more and more features are added, the program usually turns into a horrible mess that is even more difficult to modify than before (although this probably increases the job security of the programmer).
To deal with this problem, SWIG can be used to reoganize your application as follows:
With this model, all of the functionality of the C program is retained, but it is accessed through a scripting language interpreter. This approach is nice because the interpreter gives you the freedom to call functions in any order, access variables, and write scripts to do all sorts of interesting things. If you want to change something, just modify a script. If you're trying to debug a particular feature, you can call functions, access variables, and slowly step through a complex process to see what happens. If you're trying to build a package out of various components, you can just glue everything together with a scripting language and have a common interface to all of the various pieces. Or, if you wanted to write a GUI, you might consider the use of a package such as Tk.
SWIG tries to make the integration between scripting languages and C as painless as possible. This allows you to focus on the underlying C program and using the high-level scripting language interface, but not the tedious and complex chore of making the two languages talk to each other.
/* File : example.c */ double My_variable = 3.0; /* Compute factorial of n */ int fact(int n) { if (n <= 1) return 1; else return n*fact(n-1); } /* Compute n mod m */ int my_mod(int n, int m) { return(n % m); }
Suppose that you wanted to access these functions and the global variable My_variable from Tcl. You start by making a SWIG interface file as shown below (by convention, these files carry a .i suffix) :
/* File : example.i */ %module example %{ /* Put headers and other declarations here */ %} extern double My_variable; extern int fact(int); extern int my_mod(int n, int m);
The interface file contains ANSI C function prototypes and variable declarations. The %module directive defines the name of the module that will be created by SWIG. The %{,%} block provides a location for inserting additional code such as C header files or additional C functions.
unix > swig -tcl example.i Generating wrappers for Tcl. unix > gcc -c -fpic example.c example_wrap.c -I/usr/local/include unix > gcc -shared example.o example_wrap.o -o example.so unix > tclsh % load ./example.so % fact 4 24 % my_mod 23 7 2 % expr $My_variable + 4.5 7.5 %
The swig command produced a new file called example_wrap.c that should be compiled along with the example.c file. Most operating systems and scripting languages now support dynamic loading of modules. In our example, our Tcl module has been compiled into a shared library that can be loaded into Tcl. When loaded, Tcl can now access the functions and variables declared in the SWIG interface. Taking a careful look at the file example_wrap.c reveals a hideous mess, but fortunately you almost never need to worry about it.
unix > swig -perl5 example.i Generating wrappers for Perl5 unix > gcc -c example.c example_wrap.c \ -I/usr/local/lib/perl5/sun4-solaris/5.003/CORE unix> ld -G example.o example_wrap.o -o example.so # This is for Solaris unix > perl5.003 use example; print example::fact(4), "\n"; print example::my_mod(23,7), "\n"; print $example::My_variable + 4.5, "\n"; <ctrl-d> 24 2 7.5 unix >
unix > swig -python example.i Generating wrappers for Python unix > gcc -c -fpic example.c example_wrap.c -I/usr/local/include/python2.0 unix > gcc -shared example.o example_wrap.o -o examplemodule.so unix > python Python 2.0 (#6, Feb 21 2001, 13:29:45) [GCC egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)] on linux2 Type "copyright", "credits" or "license" for more information. >>> import example >>> example.fact(4) 24 >>> example.my_mod(23,7) 2 >>> example.cvar.My_variable + 4.5 7.5
% swig -perl5 -module example example.c unix > gcc -c example.c example_wrap.c \ -I/usr/local/lib/perl5/sun4-solaris/5.003/CORE unix> ld -G example.o example_wrap.o -o example.so unix > perl5.003 use example; print example::fact(4), "\n"; print example::my_mod(23,7), "\n"; print $example::My_variable + 4.5, "\n"; <ctrl-d> 24 2 7.5
Of course, there are some restrictions as SWIG is not a full C/C++ parser. If you make heavy use of the C preprocessor, complicated declarations, or C++, giving SWIG a raw source file probably isn't going to work very well (in this case, you would probably want to use a separate interface file).
SWIG provides its own version of the C preprocessor. Thus, if you want to make a combination SWIG/C header file, you might write the following:
/* File : example.h */ #ifdef SWIG %module example %include tclsh.i #endif extern double My_variable; extern int fact(int); extern int my_mod(int n, int m);
In a large system, an interface might be built from a variety of pieces like this :%module foo %include example.i // Get definitions from example.i ... Now more declarations ...
SWIG comes with a library of existing functions known as the SWIG library. The library contains a mix of language independent and language dependent functionality. For example, the file `array.i' provides access to C arrays while the file `wish.i' includes specialized code for rebuilding the Tcl wish interpreter. Using the library, you can use existing modules to build up your own personalized environment for building interfaces. If changes are made to any of the components, they will appear automatically the next time SWIG is run.%module package %include network.i %include file.i %include graphics.i %include objects.i %include simulation.i
SWIG doesn't know if a is an array of some fixed size, a dynamically allocated block of memory, a single value, or an output value of the function. For the most part, SWIG takes a literal approach (a is obviously a double *) and leaves it up to the user to use the function in a correct manner. Fortunately, there are several ways to help SWIG out using additional directives and "helper" functions. Thus, the input to SWIG is often a mix of C declarations, special directives and hints. Like Perl, there is almost always more than one way to do almost anything.int foo(double *a);
SWIG does not currently parse every conceivable type of C declaration that it might encounter in a C/C++ file. Many things may be difficult or impossible to integrate with a scripting language. Thus, SWIG may not recognize certain advanced C/C++ constructs. Of course, SWIG's parser is always being improved so currently unsupported features may be supported in later releases.
While you may not be using SWIG to develop a GUI, the underlying concept is the same. The scripting language acts as an event-loop waiting for you to issue commands. Unlike a traditional C application (that may use command line options), there is no longer a fixed sequence of operations. Functions may be issued in any order and at any time. Of course, this flexibility is exactly what we want!
However, there are a number of things to keep in mind when developing an application with SWIG :