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);
}
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.
// 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
There are several things to notice about this file
// 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
# 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)
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.
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. >>>
# 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`
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.
// 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);
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 :
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
>>>