[ Table of Contents ] [ Front Page ] [ Prev ] [ Linux Gazette FAQ ] [ Next ]

"Linux Gazette...making Linux just a little more fun!"


Using SWIG to interface scripting languages with C/C++

By Pramode C.E and Gopakumar C.E


Introduction

Scripting languages like Perl, Python and Tcl are receiving a lot of attention nowadays - mainly because these languages facilitate Rapid Application Development and Prototyping. It has been shown time and again that using a language like Python cuts down development time drastically - with the added advantage that you get highly robust and flexible code. But there are situations in which a pure scripting approach does not work, typical examples being scientific applications which require high speed math and graphics routines or applications which need to control and coordinate hardware devices on a real time basis. What we need is a mixed-language paradigm in which traditional systems languages like C/C++ do the 'dirty' low-level work while the Scripting language acts as the overall supervisor. This article focuses on using an excellent program called the Simplified Wrapper and Interface Generator (SWIG) to integrate code written in C/C++ with the popular scripting language Python. The code fragments in this article have been tested on a Red Hat Linux (5.2) machine running Python ver 1.5.1.

Getting and Installing SWIG

SWIG is being developed by Dave Beazley and can be downloaded from www.swig.org. Installation is straightforward, just run ./configure and then make. Currently, SWIG supports Perl, Python, Tcl and FSF Guile.

A simple example

Let us say we have a C function called add(a, b) which returns the sum of two numbers passed to it as arguments. We will see how the add function can be made Python-callable. We will create a file called arith.c which contains the following code:


int add(int a, int b)
{
		return a+b;
}

Let us now execute the following command:

        swig -python -module arith arith.c
		
We see that SWIG has created two new files, arith_wrap.c and arith_wrap.doc. We should now compile both arith.c and arith_wrap.c to produce object files, the command being:

        gcc -I/usr/include/python1.5 -c arith.c arith_wrap.c
	
The object files arith.o and arith_wrap.o should now be combined to produced a shared object called arith.so:

        ld -shared -o arith.so arith.o arith_wrap.o
	
If everything goes well, we will have a file called arith.so in our current directory. Here is a sample interaction using the arith module:

import arith
>>>arith.add(10, 20)
30
>>>arith.add(10, -10)
0
>>>

Does using C improve speed?

We will find out! Let us add one more function to our arith.c file and then rebuild arith.so:


int fact (int n)
{
		int f=1;
		while (n > 1){
				f = f * n;
				n = n - 1;
		}
		return f;
}

Let us make a similar function in Python (store it in a file fact.py):

def fact(n):
	f = 1
	while n > 1:
		f = f * n
		n = n - 1
	return f
	
We will now write a crude profiling program, profile.py:

#!/usr/bin/python

import fact, arith, time

pyfact = fact.fact
cfact = arith.fact

# Measuring speed of cfact
start = time.time()
for i in range(1,100000):
	cfact(10)
end = time.time()

print 'C factorial function used', end-start, 'seconds'

start = time.time()
for i in range(1,100000):
	pyfact(10)
end = time.time()

print 'Python factorial function used', end-start, 'seconds'

Here is the output on our old Pentium box:


C factorial function used 1.29531896114 seconds
Python factorial function used 8.22897398472 seconds

The proper way of using SWIG

SWIG generates wrappers not by looking at how your C code works internally, but by seeing the interface specification. Here is how we should have proceeded. First, create an interface file, arith.i. The file should contain the following lines:


%module arith

extern int add(int a, int b);
extern int fact(int n);

Now generate the wrappers by running swig -python arith.i, compile into object files and use ld to create arith.so.

A low-level interfacing example

The PC's parallel port can be used to perform some very amusing hardware interfacing experiments. On Linux, we have functions like inb(), outb() etc which can be used to access I/O ports. Here is a C program which writes to the printer port:


#include <asm/io.h>
int main()
{
		iopl(3);
		outb(1, 0x378);
}

The program should be compiled as cc -O2 io.c and it should be executed with superuser privilege. The iopl call is required to make the IO ports accessible to user programs. How do we write a Python version of this program? Simple. Use SWIG to make Python callable versions of outb(), inb() and iopl(). Here is an io.c module tailored for SWIG:

#include <asm/io.h> 

int py_iopl(int level)
{
		return iopl(level);
}

void py_outb(unsigned char val, int port)
{
		
		outb(val, port);
}

unsigned char py_inb(int port)
{
		return inb(port);
}

Run SWIG and generate the io.so file. Here is a sample interaction (remember, you should run the Python interpreter as root):
>>>import io
>>>io.py_iopl(3)
>>>io.py_outb(10, 0x378)
>>>io.py_inb(0x378)
10
>>>

Accessing C variables

Global variables declared in your C module can be accessed from Python. We create a module example with two variables foo and baz:


int foo = 10;
int baz = 20;
	
The variables foo and baz are accessible in Python through an object called cvar:

>>>import example
>>>example.cvar
Global variables { foo, baz }
>>>example.cvar.foo
10
>>>example.cvar.baz
20
>>>example.cvar.foo = 100
>>>example.cvar.foo
100
>>>

Accessing C++ Classes

Accessing C++ classes is a bit tricky. Let us first create a header file with a simple class declaration. We call this zoo.h:


class Zoo{
		int n;
		char animals[10][50];
public:
		Zoo();
		void shut_up(char *animal);
		void display();
};

Now we create an interface file zoo.i:

%module zoo

%{
#include "zoo.h"
%}

class Zoo{
	char animals[10][50];
	int n;
public:
	 Zoo();
	 void shut_up(char *animal);
	 void display();
};

We generate the Python wrappers by running the command:
        swig -python -c++ zoo.i
		
Here comes our source file zoo.cc:

#include "zoo.h"
#include <stdio.h>

Zoo::Zoo()
{
		n = 0;
}

void Zoo::shut_up(char *animal)
{
		if (n < 10) {
				strcpy(animals[n], animal);
				n++;
		}
}

void Zoo::display()
{
		int i;
		for(i = 0; i < n; i++)
				printf("%s\n", animals[i]);
}

We create the object files by running:

        g++ -I/usr/include/python1.5 -c zoo.cc zoo_wrap.c
		
And finally, we create the module zoo.so:

        ld -shared  -o zoo.so zoo.o zoo_wrap.o /usr/lib/libg++.so.2.7.2 /usr/lib/libstdc++.so
		
Here is an interactive Python session with our zoo module:

Script started on Mon Dec 13 14:31:26 1999
[pce@bhim] ~/src/writings/swig/src/shadow$ python
Python 1.5.1 (#1, Sep  3 1998, 22:51:17)  [GCC 2.7.2.3] on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import zoo
>>> dir(zoo)
['Zoo_display', 'Zoo_shut_up', '__doc__', '__file__', '__name__', 'new_Zoo']
>>> z=zoo.new_Zoo()
>>> zoo.Zoo_shut_up(z,'Tiger')
>>> zoo.Zoo_shut_up(z,'Lion')
>>> zoo.Zoo_display(z)
Tiger
Lion
>>> z2=zoo.new_Zoo()
>>> zoo.Zoo_shut_up(z2,'Python')
>>> zoo.Zoo_display(z2)
Python
>>>

The constructor in our class Zoo has been mapped to a function called new_Zoo. Similarly, the member functions shut_up and display have been mapped to Zoo_shut_up and Zoo_display.

Shadow classes

It is possible to create Python classes which act as 'wrappers' around C++ classes. Here is a Python wrapper class for the C++ Zoo class:


from zoo import *

class Zoo:
	def __init__(self):
		self.this = new_Zoo()
		
	def shut_up(self, animal):
		Zoo_shut_up(self.this, animal)
		
	def display(self):
		Zoo_display(self.this)
		

Now, we can very easily write:

>>> z = Zoo()
>>> z.shut_up('Tiger')
>>> z.shut_up('Lion')
>>> z.display()
Tiger
Lion
>>>

It is even possible to request SWIG to generate Python shadow classes automatically!

Conclusion

SWIG is a useful tool, easy to learn and easy to apply. Though we have only examined SWIG in the context of Python scripting, usage with other languages like Perl and Tcl is very similar. The SWIG Home Page is the definitive source for more information.


Copyright © 2000, Pramode C.E and Gopakumar C.E
Published in Issue 49 of Linux Gazette, January 2000


[ Table of Contents ] [ Front Page ] [ Prev ] [ Linux Gazette FAQ ] [ Next ]