Bindings Python and C

Goal

Our goal is to be able to call C functions from Python.

  • Hello, I am a C programmer and I built a super-fast library to detect collisions between different shapes.
  • Excellent! I am a Python programmer. Can I use your library?
  • Sure, I also programmed bindings for it.

General idea

C code

Consider a C program:

int mysuperfunction(int x)
{
    return 2 * x;
}

Compile a shared library

A shared library is a library that can be loaded dynamically (at the execution time). To do that:

gcc faslibrary.c -shared -o fastlibrary.so

In Python

```python

import ctypes

def getFastlibrary(): fastlibrary = ctypes.CDLL("./fastlibrary.so") # load the shared library return fastlibrary

def main(): fastlibrary = getFastlibrary() print(fastlibrary.mysuperfunction(21)) # call to the function written in C :)




By default, `ctypes` assumes that functions takes `int` and returns `int`...



## Primitive types

We can inform the types of the argument and of the returned value.

- The C function `mysuperfunction` takes an int as an input:

```python
fastlibrary.mysuperfunction.argtypes = [ctypes.c_int]
  • The C function mysuperfunction returns an int:
fastlibrary.mysuperfunction.argtypes = ctypes.c_int
  • The C function beautify takes a char * an input:
fastlibrary.beautify.argtypes = [ctypes.c_char_p]

The usage is subtle:

fastlibrary.beautify.restype = ctypes.c_char_p

s = "AVEC".encode('utf-8') # in order to be able to get a char*
print(fastlibrary.beautify(s))	

https://en.wikibooks.org/w/index.php?title=Python_Programming%2FExtending_with_ctypes

Structures

Declaration

For this C structure

struct Point {
    int x;
    int y;
};

we declare this class in Python:

class Point(ctypes.Structure):
    _fields_ = (
        ("x", ctypes.c_int),
        ("y", ctypes.c_int),
    )

### Usage

Our super C function computes the norm of a vector/point:

float norm(struct Point p) {
    return sqrt(p.x * p.x + p.y * p.y);
}

For the types we do:

fastlibrary.norm.argtypes = [Point]
fastlibrary.norm.restype = ctypes.c_float

For calling the C function, we do:

p = Point(0, 4)
p.x = 3
print(fastlibrary.norm(p))

Arrays and pointers

Suppose we want to be able to run:

/**
 * @brief compute the integer barycenter
 * 
 * @param n nb of points
 * @param points (array of length n) 
 * @return the integer barycenter 
 */
struct Point barycenter(int n, struct Point *p)
{
    struct Point b = {.x = 0, .y = 0};

    for (int i = 0; i < n; i++)
    {
        b.x += p[i].x;
        b.y += p[i].y;
    }

    b.x /= n;
    b.y /= n;

    return b;
}

We declare the types as follows:

fastlibrary.barycenter.argtypes = [ctypes.c_int, ctypes.POINTER(Point)]
fastlibrary.barycenter.restype = Point

A Python list needs to be converted to C arrays.

pythonList = [Point(0, 4), Point(2, 0)]
length = len(pythonList)
Carray = (Point * length)(*pythonList)

The class Point represents struct Point. The expression (Point * length) represents the type "length continuous struct Point". We can directly feel it by passing *pythonList.

Finally we call:

result = fastlibrary.barycenter(length, Carray)
print(result.x, result.y)

We can create an non initialized C array as follows:

Carray = (Point * length)()
Carray[i] = ...