Section 22.8. A C Extension Module String Stack


22.8. A C Extension Module String Stack

Let's kick it up another notchthe following C extension module implements a stack of strings for use in Python scripts. Example 22-15 demonstrates additional API calls, but it also serves as a basis of comparison. It is roughly equivalent to the Python stack module we coded earlier in Chapter 20, but it stacks only strings (not arbitrary objects), has limited string storage and stack lengths, and is written in C.

Alas, the last point makes for a complicated program listingC code is never quite as nice to look at as equivalent Python code. C must declare variables, manage memory, implement data structures, and include lots of extra syntax. Unless you're a big fan of C, you should focus on the Python interface code in this file, not on the internals of its functions.

Example 22-15. PP3E\Integrate\Extend\Stacks\stackmod.c

 /*****************************************************  * stackmod.c: a shared stack of character-strings;  * a C extension module for use in Python programs;  * linked into Python libraries or loaded on import;  *****************************************************/ #include "Python.h"             /* Python header files */ #include <stdio.h>              /* C header files */ #include <string.h> static PyObject *ErrorObject;   /* locally raised exception */ #define onError(message) \        { PyErr_SetString(ErrorObject, message); return NULL; } /****************************************************************************** * LOCAL LOGIC/DATA (THE STACK) ******************************************************************************/ #define MAXCHARS 2048 #define MAXSTACK MAXCHARS static int  top = 0;                 /* index into 'stack' */ static int  len = 0;                 /* size of 'strings' */ static char *stack[MAXSTACK];        /* pointers into 'strings' */ static char strings[MAXCHARS];       /* string-storage area */ /****************************************************************************** * EXPORTED MODULE METHODS/FUNCTIONS ******************************************************************************/ static PyObject * stack_push(PyObject *self, PyObject *args)       /* args: (string) */ {     char *pstr;     if (!PyArg_ParseTuple(args, "s", &pstr))     /* convert args: Python->C */         return NULL;                             /* NULL triggers exception */     if (top == MAXSTACK)                         /* Python sets arg-error msg */         onError("stack overflow")                /* iff maxstack < maxchars */     if (len + strlen(pstr) + 1 >= MAXCHARS)         onError("string-space overflow")     else {         strcpy(strings + len, pstr);             /* store in string-space */         stack[top++] = &(strings[len]);          /* push start address */         len += (strlen(pstr) + 1);               /* new string-space size */         Py_INCREF(Py_None);                      /* a 'procedure' call */         return Py_None;                          /* None: no errors */     } } static PyObject * stack_pop(PyObject *self, PyObject *args) {                                                /* no arguments for pop */     PyObject *pstr;     if (!PyArg_ParseTuple(args, ""))             /* verify no args passed */         return NULL;     if (top == 0)         onError("stack underflow")               /* return NULL = raise */     else {         pstr = Py_BuildValue("s", stack[--top]); /* convert result: C->Py */         len -= (strlen(stack[top]) + 1);         return pstr;                             /* return new Python string */     }                                            /* pstr ref-count++ already */ } static PyObject * stack_top(PyObject *self, PyObject *args)        /* almost same as item(-1) */ {                                                /* but different errors */     PyObject *result = stack_pop(self, args);    /* get top string */     if (result != NULL)         len += (strlen(stack[top++]) + 1);       /* undo pop */     return result;                               /* NULL or string object */ } static PyObject * stack_empty(PyObject *self, PyObject *args)      /* no args: '( )' */ {     if (!PyArg_ParseTuple(args, ""))             /* or PyArg_NoArgs */         return NULL;     return Py_BuildValue("i", top == 0);         /* Boolean: a Python int */ } static PyObject * stack_member(PyObject *self, PyObject *args) {     int i;     char *pstr;     if (!PyArg_ParseTuple(args, "s", &pstr))         return NULL;     for (i = 0; i < top; i++)                /* find arg in stack */         if (strcmp(pstr, stack[i]) == 0)             return PyInt_FromLong(1);        /* send back a Python int */     return PyInt_FromLong(0);                /* same as Py_BuildValue("i" */ } static PyObject * stack_item(PyObject *self, PyObject *args)    /* return Python string or NULL */ {                                             /* inputs = (index): Python int */     int index;     if (!PyArg_ParseTuple(args, "i", &index))    /* convert args to C */         return NULL;                             /* bad type or arg count? */     if (index < 0)         index = top + index;                     /* negative: offset from end */     if (index < 0 || index >= top)         onError("index out-of-bounds")           /* return NULL = 'raise' */     else         return Py_BuildValue("s", stack[index]); /* convert result to Python */ }                                                /* no need to INCREF new obj */ static PyObject * stack_len(PyObject *self, PyObject *args)     /* return a Python int or NULL */ {                                             /* no inputs */     if (!PyArg_ParseTuple(args, ""))         return NULL;     return PyInt_FromLong(top);               /* wrap in Python object */ } static PyObject * stack_dump(PyObject *self, PyObject *args)    /* not "print": reserved word */ {     int i;     if (!PyArg_ParseTuple(args, ""))         return NULL;     printf("[Stack:\n");     for (i=top-1; i >= 0; i--)                   /* formatted output */         printf("%d: '%s'\n", i, stack[i]);     printf("]\n");     Py_INCREF(Py_None);     return Py_None; } /****************************************************************************** * METHOD REGISTRATION TABLE: NAME-STRING -> FUNCTION-POINTER ******************************************************************************/ static struct PyMethodDef stack_methods[] = {  {"push",       stack_push,     1},                /* name, address */  {"pop",        stack_pop,      1},                /* '1'=always tuple args */  {"top",        stack_top,      1},  {"empty",      stack_empty,    1},  {"member",     stack_member,   1},  {"item",       stack_item,     1},  {"len",        stack_len,      1},  {"dump",       stack_dump,     1},  {NULL,         NULL}                              /* end, for initmodule */ }; /****************************************************************************** * INITIALIZATION FUNCTION (IMPORT-TIME) ******************************************************************************/ void initstackmod( ) {     PyObject *m, *d;     /* create the module and add the functions */     m = Py_InitModule("stackmod", stack_methods);        /* registration hook */     /* add symbolic constants to the module */     d = PyModule_GetDict(m);     ErrorObject = Py_BuildValue("s", "stackmod.error");  /* export exception */     PyDict_SetItemString(d, "error", ErrorObject);       /* add more if need */     /* check for errors */     if (PyErr_Occurred( ))         Py_FatalError("can't initialize module stackmod"); } 

This C extension file is compiled and statically or dynamically linked with the interpreter, just like in previous examples. The file makefile.stack in this book's examples distribution handles the build with a rule like this:

 stackmod.dll: stackmod.c         gcc stackmod.c -g -I$(PYINC) -shared -L$(PYLIB) -lpython2.4 -o $@ 

The whole point of implementing such a stack in a C extension module (apart from demonstrating API calls in a Python book) is optimization: in theory, this code should present a similar interface to the Python stack module we wrote earlier, but it should run considerably faster due to its C coding. The interface is roughly the same, though we've sacrificed some Python flexibility by moving to Cthere are limits on size and stackable object types:

 .../PP3E/Integrate/Extend/Stacks$ python >>> import stackmod                                      # load C module >>> stackmod.push('new')                                 # call C functions >>> stackmod.dump( )                                          # dump format differs [Stack: 0: 'new' ] >>> for c in "SPAM": stackmod.push(c) ... >>> stackmod.dump( ) [Stack: 4: 'M' 3: 'A' 2: 'P' 1: 'S' 0: 'new' ] >>> stackmod.len(), stackmod.top( ) (5, 'M') >>> x = stackmod.pop( ) >>> x 'M' >>> stackmod.dump( ) [Stack: 3: 'A' 2: 'P' 1: 'S' 0: 'new' ] >>> stackmod.push(99) Traceback (most recent call last):   File "<stdin>", line 1, in ? TypeError: argument 1 must be string, not int 

Some of the C stack's type and size limitations could be removed by alternate C coding (which might eventually create something that looks and performs almost exactly like a Python built-in list). Before we check on this stack's speed, though, we'll see what can be done about also optimizing our stack classes with a C type.

22.8.1. But Don't Do That EitherSWIG

You can manually code extension modules like this, but you don't necessarily have to. As we saw earlier, if you instead code the stack module's functions without any notion of Python integration, they can be integrated into Python automatically by running their type signatures through SWIG. I haven't coded these functions that way here, because I also need to teach the underlying Python C extension API. But if I were asked to write a C string stack for Python in any other context, I'd probably do it with SWIG instead.




Programming Python
Programming Python
ISBN: 0596009259
EAN: 2147483647
Year: 2004
Pages: 270
Authors: Mark Lutz

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net