While writing a single wrapper function isn't too difficult, it quickly becomes tedious and error prone as the number of functions increases. SWIG automates this process by generating wrapper code from a list ANSI C function and variable declarations. As a result, adding scripting languages to C applications can become an almost trivial exercise.static PyObject *wrap_getenv(PyObject *self, PyObject *args) { char * result; char * arg0; if(!PyArg_ParseTuple(args, "s",&arg0)) return NULL; result = getenv(arg0); return Py_BuildValue("s", result); }
There are several things to notice about this file// socket.i // SWIG Interface file to play with some sockets %init sock // Name of our module %{ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> /* Set some values in the sockaddr_in structure */ struct sockaddr *new_sockaddr_in(short family, unsigned long hostid, int port) { struct sockaddr_in *addr; addr = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in)); bzero((char *) addr, sizeof(struct sockaddr_in)); addr->sin_family = family; addr->sin_addr.s_addr = hostid; addr->sin_port = htons(port); return (struct sockaddr *) addr; } /* Get host address, but return as a string instead of hostent */ char *my_gethostbyname(char *hostname) { struct hostent *h; h = gethostbyname(hostname); if (h) return h->h_name; else return ""; } %} // Add these constants enum {AF_UNIX, AF_INET, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, IPPROTO_UDP, IPPROTO_TCP, INADDR_ANY}; #define SIZEOF_SOCKADDR sizeof(struct sockaddr) // Wrap these functions int socket(int family, int type, int protocol); int bind(int sockfd, struct sockaddr *myaddr, int addrlen); int connect(int sockfd, struct sockaddr *servaddr, int addrlen); int listen(int sockfd, int backlog); int accept(int sockfd, struct sockaddr *peer, %val int *addrlen); int close(int fd); struct sockaddr *new_sockaddr_in(short family, unsigned long, int port); %name gethostbyname { char *my_gethostbyname(char *); } unsigned long inet_addr(const char *ip); %include unixio.i
// File : unixio.i // Some file I/O and memory functions %{ %} int read(int fd, void *buffer, int n); int write(int fd, void *buffer, int n); typedef unsigned int size_t; void *malloc(size_t nbytes);
The wrap command takes the interface file and produces a C file called socket_wrap.c . This file is then compiled and built into a shared object file.unix > wrap -python socket.i unix > gcc -c socket_wrap.c -I/usr/local/include/Py unix > ld -G socket_wrap.o -lsocket -lnsl -o sockmodule.so
In our Python module, we are manipulating a socket, in almost exactly the same way as would be done in a C program. We'll take a look at a client written using the Python socket library shortly.# Echo server program using SWIG module from sock import * PORT = 5000 sockfd = socket(AF_INET, SOCK_STREAM, 0) if sockfd < 0: raise IOError, 'server : unable to open stream socket' addr = new_sockaddr_in(AF_INET,INADDR_ANY,PORT) if (bind(sockfd, addr, SIZEOF_SOCKADDR)) < 0 : raise IOError, 'server : unable to bind local address' listen(sockfd,5) client_addr = new_sockaddr_in(AF_INET, 0, 0) newsockfd = accept(sockfd, client_addr, SIZEOF_SOCKADDR) buffer = malloc(1024) while 1: nbytes = read(newsockfd, buffer, 1024) if nbytes > 0 : write(newsockfd,buffer,nbytes) else : break close(newsockfd) close(sockfd)
SWIG's run-time type checking provides a certain degree of safety from using invalid parameters and making simple mistakes. Of course, any system supporting pointers can be abused, but SWIG's implementation has proven to be quite reliable under normal use.unix > python Python 1.3 (Apr 12 1996) [GCC 2.5.8] Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> from sock import * >>> sockfd = socket(AF_INET, SOCK_STREAM,0); >>> buffer = malloc(8192); >>> bind(sockfd,buffer,SIZEOF_SOCKADDR); Traceback (innermost last): File "", line 1, in ? TypeError: Type error in argument 2 of bind. Expected _struct_sockaddr_p. >>>
As one might expect, the two scripts look fairly similar. However there are certain obvious differences. First, the SWIG generated module is clearly a quick and dirty approach. We must explicitly check for errors and create a few C data structures. Furthermore, while Python functions such as socket() can take optional arguments, SWIG generated functions can not (since the underlying C function can't). Of course, the apparent ugliness of the SWIG module compared to the Python equivalent is not the point here. The real idea to emphasize is that SWIG can take a set of real C functions with moderate complexity and produce a fully functional Python module in a very short period of time. In this case, about 10 minutes worth of effort. (I'm guessing that the Python socket module required more time than that). With a bit of work, the SWIG generated module could probably be used to build a more user-friendly version that hid many of the details.# Echo client program from socket import * HOST = 'tjaze.lanl.gov' PORT = 5000 s = socket(AF_INET, SOCK_STREAM) s.connect(HOST,PORT) s.send('Hello world') data = s.recv(1024) s.close() print 'Received', `data`
SWIG has no idea what Tcl_Interp is, but it doesn't really care as long as it's used as a pointer. When used in a Python script, this module would work as follows :// tcl.i // Wrap Tcl's C interface %init tcl %{ #include <tcl.h> %} Tcl_Interp *Tcl_CreateInterp(void); void Tcl_DeleteInterp(Tcl_Interp *interp); int Tcl_Eval(Tcl_Interp *interp, char *script); int Tcl_EvalFile(Tcl_Interp *interp, char *file);
unix > python Python 1.3 (Mar 26 1996) [GCC 2.7.0] Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> import tcl >>> interp = tcl.Tcl_CreateInterp(); >>> tcl.Tcl_Eval(interp,"for {set i 0} {$i < 5} {incr i 1} {puts $i}"); 0 1 2 3 4 0 >>>