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 achar *
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] = ...