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