Using C codes in Python | Set 1

Prerequisite: How to Call a C function in Python

Let’s discuss the problem of accessing C code from Python. As it is very evident that many of Python’s built-in libraries are written in C. So, to access C is a very important part of making Python talk to existing libraries. There is an extensive C programming API that Python provides but there are many different to deal with C.

Code #1 : [work.c] C-Code that we are dealing.

filter_none

edit
close

play_arrow

link
brightness_4
code

#include <math.h>
  
int gcd(int x, int y)
{
    int g = y;
    while (x > 0)
    {
        g = x;
        x = y % x;
        y = g;
    }
    return g;
}
  
int divide(int a, int b, int * remainder)
{
    int quot = a / b;
    *remainder = a % b;
    return quot;
}
  
double avg(double * a, int n)
{
    int i;
    double total = 0.0;
    for (i = 0; i < n; i++)
    {
        total += a[i];
    }
    return total / n;
}
  
typedef struct Point
{
    double x, y;
} Point;
  
double distance(Point * p1, Point * p2)
{
    return hypot(p1->x - p2->x, p1->y - p2->y);
}

chevron_right


Above code has different C-programming features.

gcd()
divide() – returning multiple values, one through a pointer argument
avg() – performing a data reduction across a C array
Point and distance() – involve C structures.



Let’s assume that the code above is found in a file named work.c and it has been compiled into a library libsample that can be linked to other C code. Now, we have a number of C functions that have been compiled into a shared library. So, we call the functions entirely from Python without having to write additional C code or using a third-party extension tool.
 
Using ctypes :
Python ctypes will come to play but make sure the C code, that is to be converted, has been compiled into a shared library that is compatible with the Python interpreter (e.g., same architecture, word size, compiler, etc.).

Further the libsample.so file has been placed in the same directory as the work.py. Let’s understand work.py now.

Code #2 : Python module that wraps around resulting library to access it

filter_none

edit
close

play_arrow

link
brightness_4
code

# work.py
import ctypes
import os
  
# locating the 'libsample.so' file in the
# same directory as this file
_file = 'libsample.so'
_path = os.path.join(*(os.path.split(__file__)[:-1] + (_file, )))
_mod = ctypes.cdll.LoadLibrary(_path)

chevron_right


Code #3 : Accessing code

filter_none

edit
close

play_arrow

link
brightness_4
code

# int gcd(int, int)
gcd = _mod.gcd
gcd.argtypes = (ctypes.c_int, ctypes.c_int)
gcd.restype = ctypes.c_int
  
# int divide(int, int, int *)
_divide = _mod.divide
_divide.argtypes = (ctypes.c_int, ctypes.c_int,
                    ctypes.POINTER(ctypes.c_int))
  
_divide.restype = ctypes.c_int
  
def divide(x, y):
    rem = ctypes.c_int()
    quot = _divide(x, y, rem)
    return quot, rem.value
  
# void avg(double *, int n)
# Define a special type for the 'double *' argument
class DoubleArrayType:
    def from_param(self, param):
          
        typename = type(param).__name__
          
        if hasattr(self, 'from_' + typename):
            return getattr(self, 'from_' + typename)(param)
          
        elif isinstance(param, ctypes.Array):
            return param
          
        else:
            raise TypeError("Can't convert % s" % typename)
  
    # Cast from array.array objects
    def from_array(self, param):
        if param.typecode != 'd':
            raise TypeError('must be an array of doubles')
          
        ptr, _ = param.buffer_info()
        return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
          
    # Cast from lists / tuples
    def from_list(self, param):
        val = ((ctypes.c_double)*len(param))(*param)
        return val
      
    from_tuple = from_list
      
    # Cast from a numpy array
    def from_ndarray(self, param):
        return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
  
DoubleArray = DoubleArrayType()
_avg = _mod.avg
_avg.argtypes = (DoubleArray, ctypes.c_int)
_avg.restype = ctypes.c_double
  
def avg(values):
    return _avg(values, len(values))
  
# struct Point { }
class Point(ctypes.Structure):
    _fields_ = [('x', ctypes.c_double), ('y', ctypes.c_double)]
      
# double distance(Point *, Point *)
distance = _mod.distance
distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
distance.restype = ctypes.c_double

chevron_right


Now, one can easily load the module and use the resulting C functions. See the next part – Using C codes in Python | Set 2.



My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.




Article Tags :

Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.