Skip to content
Related Articles

Related Articles

Improve Article
Save Article
Like Article

Cython to Wrap Existing C Code

  • Last Updated : 29 Mar, 2019

What is Cython ?
It is an optimizing static compiler for both the Python programming language and the extended Cython programming language. It is used to make it easy to write C extensions for Python as easy as Python itself.

It comes up with many helpful features :

 Attention geek! Strengthen your foundations with the Python Programming Foundation Course and learn the basics.  

To begin with, your interview preparations Enhance your Data Structures concepts with the Python DS Course. And to begin with your Machine Learning Journey, join the Machine Learning - Basic Level Course

  • Writing a Python code that calls back and forth from and to C/C++ code.
  • Easily tuning of readable Python code into plain C performance by adding static type declarations.
  • Use of combined source code level debugging to find bugs in given Python, Cython and C code.
  • Efficient interaction with large data sets, e.g. using multi-dimensional NumPy arrays.
  • Integration with existing code and data from low-level or high-performance libraries and applications.

To make an extension with Cython is a tricky task to perform. Doing so, one needs to create a collection of wrapper functions. Assuming that the work code shown has been compiled into a C library called libwork. The code below will create a file named csample.pxd.



Code #1 :




# cwork.pxd
#
# Declarations of "external" C 
# functions and structures
  
cdef extern from "work.h":
   
    int gcd(int, int)
    int divide(int, int, int *)
    double avg(double *, int) nogil
      
    ctypedef struct Point:
        double x
        double y
          
    double distance(Point *, Point *)

In Cython, the code above will work as a C header file. The initial declaration cdef extern from "work.h" declares the required C header file. Declarations that follow are taken from the header. The name of this file is cwork.pxd. Next target is to create a work.pyx file which will define wrappers that bridge the Python interpreter to the underlying C code declared in the cwork.pxd file.

Code #2 :




# work.pyx
# Import the low-level C declarations
        
cimport cwork
# Importing functionalities from Python
# and the C stdlib
from cpython.pycapsule cimport * 
from libc.stdlib cimport malloc, free
  
# Wrappers
def gcd(unsigned int x, unsigned int y):
    return cwork.gcd(x, y)
  
def divide(x, y):
    cdef int rem
    quot = cwork.divide(x, y, &rem)
    return quot, rem
  
def avg(double[:] a):
    cdef:
        int sz
        double result
  
    sz = a.size
  
    with nogil:
        result = cwork.avg(<double *> &a[0], sz)
  
    return result

 
Code #3 :




# Destructor for cleaning up Point objects
cdef del_Point(object obj):
    pt = <csample.Point *> PyCapsule_GetPointer(obj, "Point")
    free(<void *> pt)
      
# Create a Point object and return as a capsule
def Point(double x, double y):
    cdef csample.Point * p
    p = <csample.Point *> malloc(sizeof(csample.Point))
      
    if p == NULL:
        raise MemoryError("No memory to make a Point")
          
    p.x = x
    p.y = y
      
    return PyCapsule_New(<void *>p, "Point",
                         <PyCapsule_Destructor>del_Point)
  
def distance(p1, p2):
    pt1 = <csample.Point *> PyCapsule_GetPointer(p1, "Point")
    pt2 = <csample.Point *> PyCapsule_GetPointer(p2, "Point")
      
    return csample.distance(pt1, pt2)

 
Finally, to build the extension module, create a work.py file.

Code #4:




# importing libraries
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
  
ext_modules = [Extension('work'
                         ['work.pyx'], 
                         libraries=['work'], 
                         library_dirs=['.'])]
  
setup(name = 'work extension module',
      cmdclass = {'build_ext': build_ext},
      ext_modules = ext_modules)

 
Code #5 : Building resulting module for experimentation.




bash % python3 setup.py build_ext --inplace
running build_ext
  
cythoning work.pyx to work.c
building 'work' extension
  
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
-I/usr/local/include/python3.3m -c work.c
-o build/temp.macosx-10.6-x86_64-3.3/work.o
  
gcc -bundle -undefined dynamic_lookup build/temp.macosx-10.6-x86_64-3.3/work.o
-L. -lwork -o work.so
bash %

Now, we have an extension module work.so. Let’s see how it works.

Code #6 :




import sample
print ("GCD : ", sample.gcd(12, 8))
  
print ("\nDivision : ", sample.divide(42,10))
  
import array
arr = array.array('d',[1,2,3])
print ("\nAverage  : ", sample.avg(a)
  
pt1 = sample.Point(2,3)
pt2 = sample.Point(4,5)
  
print ("\npt1 : ", pt1)
print ("\npt2 : ", pt2)
  
print ("\nDistance between the two points : "
       sample.distance(pt1, pt2))

Output :

GCD : 4

Division : (4, 2)

Average : 2.0

pt1 : <capsule object "Point" at 0x1005d1e70>

pt2 : <capsule object "Point" at 0x1005d1ea0>

Distance between the two points : 2.8284271247461903

At a high level, using Cython is modeled after C. The .pxd files merely contain C definitions (similar to .h files) and the .pyx files contain implementation (similar to a .c file). The cimport statement is used by Cython to import definitions from a .pxd file. This is different than using a normal Python import statement, which would load a regular Python module.




My Personal Notes arrow_drop_up
Recommended Articles
Page :

Start Your Coding Journey Now!