Prerequisite: Using C codes in Python, Wrapping C/C++ for Python using SWIG
Suppose we have given a C code and it needs to be accessed as a C extension module. So, for the given task – Swig Wrapper Generator is used.
Swig operates by parsing C header files and automatically creating extension code. C-header file is needed first, to use Swig. Give an example of C-header file in the code below.
Code #1 : work.h
// work.h # include <math.h> extern int gcd(int, int); extern int divide(int a, int b, int * remainder); extern double avg(double * a, int n); typedef struct Point { double x, y; } Point; extern double distance(Point * p1, Point * p2); |
After having the header file, the next step is to write a Swig “interface” file. By convention, these files have a .i suffix and might look similar to the following.
Code #2 : work.i
// work.i - Swig interface % module work % { # include "work.h" % } // Customizations % extend Point { // Constructor for Point objects Point(double x, double y) { Point * p = (Point *) malloc(sizeof(Point)); p->x = x; p->y = y; return p; }; }; |
Code #3 : Mapping
// Map int * remainder as an output argument %include typemaps.i %apply int * OUTPUT { int * remainder }; // Map the argument pattern (double * a, int n) to arrays %typemap(in) (double * a, int n)(Py_buffer view) { view.obj = NULL; if (PyObject_GetBuffer($input, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) { SWIG_fail; } if (strcmp(view.format, "d") != 0) { PyErr_SetString(PyExc_TypeError, "Expected an array of doubles"); SWIG_fail; } $1 = (double *) view.buf; $2 = view.len / sizeof(double); } % typemap(freearg) (double * a, int n) { if (view$argnum.obj) { PyBuffer_Release(&view$argnum); } } |
Once the interface file is ready, Swig is invoked as a command-line tool
Code #4 :
bash % swig -python -py3 work.i bash % |
The output of swig is two files – work_wrap.c and work.py. work.py file is what users import and the work_wrap.c file is C code that needs to be compiled into a supporting module called _work. It is performed using the same techniques as for normal extension modules. For example, creating a setup.py file as shown in the code below –
Code #5 :
# setup.py from distutils.core import setup, Extension setup(name='sample', py_modules=['sample.py'], ext_modules=[ Extension( '_sample', ['sample_wrap.c'], include_dirs = [], define_macros = [], undef_macros = [], library_dirs = [], libraries = ['sample'] ) ] ) |
Code #6 : Compile and test, run python3 on the setup.py
bash % python3 setup.py build_ext --inplace running build_ext building '_sample' extension gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/usr/local/include/python3.3m -c work_wrap.c -o build/temp.macosx-10.6-x86_64-3.3/work_wrap.o work_wrap.c: In function ‘SWIG_InitializeModule’: work_wrap.c:3589: warning: statement with no effect gcc -bundle -undefined dynamic_lookup build/temp.macosx-10.6-x86_64-3.3/work.o build/temp.macosx-10.6-x86_64-3.3/work_wrap.o -o _work.so -lwork bash % |
After performing all the tasks, we can use the C extension module in a very easy way.
Code #7 :
import work print ("GCD : ", work.gcd(12,8)) print ("\nDivision : ", work.divide(42,8)) pt1 = work.Point(2,3) pt2 = work.Point(4,5) print ("\nDistance between pt1 and pt2 : ", work.distance(pt1,pt2)) print ("\nx co-ordinate of pt1 : ", pt1.x) print ("\ny co-ordinate of pt1 : ", pt1.x) import array ar = array.array('d',[2, 4, 6]) print ("\nAverage : ", work.avg(arr)) |
Output :
GCD : 4 Divide : [5, 2] Distance between pt1 and pt2 : 2.8284271247461903 Distance between pt1 and pt2 : 2.0 Distance between pt1 and pt2 : 3.0 Average : 4.0
