ePolyglot: Examination and development of multilanguage programming using Eiffel, Python, and Haskell | ||
---|---|---|
Prev | Chapter 6. Wrapping the Python/C API | Next |
SWIG is actually very straightforward to use, although first attempts ran into a few troubles. While SWIG can be used on header files directly, this results in some problems with Cozzi's Eiffel module--more specifically, it does not have any way to specify the "module" that SWIG will created (in Eiffel, this translates into the class that will be generated when all wrapping is complete). In order to properly generate the module, we had to create a separate interface file for SWIG.
This in itself was not such a big deal, but it was extremely time-consuming. Essentially, making a SWIG interface file consists of writing a short text file detailing the module name and all exported functions. For example, the first part of the hand-generated python_client.i file reads as follows:
%module python_client %{ #include <Python.h> %} /*Initialization, finalization*/ extern void Py_Initialize( void ); int Py_IsInitialized( void ); extern void Py_Finalize( void ); PyThreadState* Py_NewInterpreter( void ); void Py_EndInterpreter( PyThreadState* tstate ); void Py_SetProgramName( char* name ); |
In other words, extremely simple to do, because all that was necessary is a straightforward listing of the C functions which were going to be exported to Python. The function headers were taken from a number of places; primarily, this was done by stepping through the Python/C API documentation for functions which looked to be useful for embedding the interpreter (the online and downloadable documentation for Python includes a nice, if incomplete for our purposes, description of embedding/extending Python). When the documentation showed any holes, or we needed a method that we suspected existed but was not documented, then it was time to page through the Python header files in search of likely-sounding functions. For the most part this worked, and we had created a fairly large interface file containing functions for:
Initialization/finalization. For starting and shutting down the Python interpreter.
The very high level layer. For simple compilation and execution of text code fragments.
Reference counting. For manipulating the reference-counting collection of Python.
Exception handling and Exceptions. For handling error conditions under Python, an area which still needs some work in the project.
OS utilities. For a few minor functions having to do with the OS and identification.
Process control. For handling fatal error and exit conditions.
Module import. For importing Python modules, an extremely important set of functions.
Abstract object protocol. For handling the base level of Python objects, from which all other Python objects are derived.
Abstract and concrete number protocols. For handling the "number" classes of Python objects--floats, integers, etc.
Abstract and concrete sequence protocols. For handling Python sequence objects--strings, lists, tuples, etc.
Abstract and concrete mapping protocols. For handling Python mapping objects--specifically, dictionaries.
Type protocols. For type checking.
File protocols. For manipulating files.
Evaluation protocols. For evaluating code snippets or calling callable objects.
During the to-date course of the ePolyglot project, we did not use all the functions we made prototypes for. There is a lot of functionality exposed in the Python/C API, and we were focused on a small subset of what was provided--most specifically, the ability to start up and shut down the interpreter, and manipulate Python objects using it. There were a few classes of functions in the Python/C API that we didn't even expose to the Eiffel interface for fear that implementing them would be more painful than it was worth--specifically, the multithreading functions and memory management functions.
After the desired functions were exposed, we had a fairly easy-to-understand interface file--it just remained for SWIG to bundle it into Eiffel-compatible code. SWIG was to handle all the type conversions automatically, particularly conversions from Eiffel data structures into C-compatible data structures. For the most part, this wasn't a problem anyway--the Python/C API functions generally took atomic values such as opaque pointers and returned the same. Our previous experiences toying with SWIG prepared us for a very easy export of the Python/C API.
We were a little surprised, then, when it refused to compile.