Wrapping C/C++ for Python using SWIG – Set 1
There is no doubt that C is faster than Python then how do Python library like Numpy perform huge number crunching job so fast and efficiently? Actually, libraries like Numpy are not completely written in Python instead, some parts of the library are written in C which provides performance boost. After writing code in C, we wrap them in Python code which acts like an interface for those C codes. We can then call C functions using Python syntax where actual processing is done in C behind the scene and the result is returned back as Python object. In this article, we will see how to create Python wrapper for our C program on Linux systems using a software called SWIG.
What is SWIG
In a nutshell, SWIG is a compiler that takes C/C++ declarations and creates a wrapper needed to access those declarations from other languages like Python, Tcl, Ruby etc.
It normally required no changes in existing code and create an interface within a minute.
Reasons for creating wrapper
In many occasions we need wrappers, following are few of them –
- Building interpreted interface for existing C programs.
- Building high-performance C modules for scripting languages
- It’s huge pain to test huge C programs, so we write wrappers in some scripting languages like Python, where it’s very easy to write test. etc
Installing SWIG
For downloading SWIG directly from apt repository type following commands –
sudo apt-get update
sudo apt-get install swig
Writing Wrapper using SWIG
Consider this piece of C code, having two functions and one global variable –
#include <stdio.h>
#include <math.h>
#include "gfg.h"
#define ll long long
double myvar = 3.4;
ll int fact(ll int n)
{
if (n <= 1)
return 1;
else
return (n * fact(n-1));
}
int my_mod( int n, int m)
{
return (n % m);
}
|
Here is our header file gfg.h –
long long int fact( long long int n);
int my_mod( int n, int m);
|
First, we have to create a SWIG Interface file. This file contains ANSI C function prototypes and variable declaration. Here –
- The %module directive specifies the name of the module we will use in Python.
- %{ .. %} block provides a location to insert additional code such as C header files or additional C declaration into the generated wrapper code.
- %include directive let us include additional files like header files.
%module gfg
%{
#include "gfg.h"
double myvar;
%}
double myvar;
long long int fact( long long int n1);
int my_mod( int m, int n);
|
Now we will create wrapper code using the command like $ swig -target_language interface_file.i
$ swig -python gfg.i
After executing this command a wrapper code with name “gfg_wrap.c” is created. This files contains a bloated version of our original C code with various error handling code etc. Another file “gfg.py” is generated which is the module we will import in our python script.
After this, we have to generate position-independent code which will be used in shared library by compiling “gfg_wrap.c” and “gfg.c” using the following command –
$ gcc -c -fpic gfg_wrap.c gfg.c -I/use/include/python2.7
Replace python2.7 with your Python version. This will generate two object files
“gfg_wrap.o” and “gfg.o”. In above command –
- -fpic generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT)
Note: If you get error something like “… ‘Python.h’ file not found” then following might be possible causes –
- You might not have ‘Python.h’ file or
- You are providing wrong location of ‘Python.h’ file to compiler
To get ‘Python.h’ You must install Python-dev using following command –
$ sudo apt-get install python-dev
To find the correct path of ‘Python.h’ execute following command –
$ python-config --cflags
This will output something like this –
Now replace the path in compilation command with this one for python2.7 or change the version as python3.5 for Python 3.5.
Now, at last, we have to link generated objects files together to create a shared object which is analogous to dll files in windows. Use the following command, this will generate a “_gfg.so” shared object file –
$ gcc -shared gfg.o gfg_wrap.o -o _gfg.so
Now we are ready to test out python wrapper by importing it. Make sure you are in directory having this wrapper file.
>>> import gfg
>>> res = fact(5)
>>> res
120
>>> res = my_mod(5,2)
>>> res
1
>>> gfg.cvar.myvar
3.4
Here C variables are accessed as module.cvar.var_name.
Compiling and Linking using distutils
Instead of typing in commands and figuring out what compilation options are needed to compile files, we can automate this using distutils. Create a setup.py as below –
from distutils.core import setup, Extension
name = "gfg"
version = "1.0"
ext_modules = Extension(name = '_gfg' ,sources = [ "gfg.i" , "gfg.c" ])
setup(name = name,
version = version,
ext_modules = [ext_modules])
|
Now write following commands to compile and install module –
$ python setup.py build_ext --inplace
It should look something like this on terminal –
Possible Alternatives
Obviously, SWIG is not the only way for creating wrappers, one can consider following alternatives based on their requirements –
In next article, we will see how to create wrapper for C++ code (OPP)
References
Last Updated :
07 Jul, 2017
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...