This will be a really short blogpost which mainly serves as a personal reminder on how to call a C function from Python… the title is pretty self-explanatory.
I will show how to call some really simple c functions wich return a double or an array (which will be a np.array in Python).
Here we have our simple c file cfunctions.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<stdlib.h>
long factorial(int n){
//
long res = 1;
if (n <= 0) {
return -1;
}
else {
for (long i = 1; i <= n; i++) {
res *= i;
}
}
return res;
}
double dotproduct(int dim, double a[dim], double b[dim]){
// Compute the dot product between two vectors...
// e.g. a = [1,2,3,4] , b = [4,3,2,1]
// res = 1*4 + 2*3 + 3*2 + 4*1 = 4 + 6 + 6 + 4 = 20
double res = 0;
for(int i = 0; i < dim; i++){
res = res + a[i]*b[i];
}
return res;
}
double * elementwiseproduct(int dim, double a[dim], double b[dim]){
// Compute the elementiwise product between two vectors...
// e.g. a = [1,2,3,4] , b = [4,3,2,1]
// res will point to an array such as [1*4, 2*3, 3*2, 4*1] = [4,6,6,4]
double * res = (double *) malloc(sizeof(double) * dim);
for(int i = 0; i < dim; i++){
res[i] = a[i]*b[i];
}
return res;
}
We then create a shared object witht the following command:
1
cc -fPIC -shared -o cfunctions.so cfunctions.c
Then we can write a Python script py_cfunctions.py such as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import ctypes
import numpy as np
def factorial(num: int):
py_cfunctions.factorial.restype = ctypes.c_long
return py_cfunctions.factorial(num)
def dotproduct(dim: int, a: np.array, b: np.array):
# Convert np.array to ctype doubles
a_data = a.astype(np.double)
a_data_p = a_data.ctypes.data_as(c_double_p)
b_data = b.astype(np.double)
b_data_p = b_data.ctypes.data_as(c_double_p)
py_cfunctions.dotproduct.restype = ctypes.c_double
# Compute result...
return py_cfunctions.dotproduct(dim,a_data_p,b_data_p)
def elementwiseproduct(dim: int, a: np.array, b: np.array):
# Convert np.array to ctype doubles
a_data = a.astype(np.double)
a_data_p = a_data.ctypes.data_as(c_double_p)
b_data = b.astype(np.double)
b_data_p = b_data.ctypes.data_as(c_double_p)
py_cfunctions.elementwiseproduct.restype = np.ctypeslib.ndpointer(dtype=ctypes.c_double,shape=(dim,))
# Compute result...
return py_cfunctions.elementwiseproduct(dim,a_data_p,b_data_p)
# so_file genreated with:
# cc -fPIC -shared -o cfunctions.so cfunctions.c
so_file = 'MY_PATH/cfunctions.so'
py_cfunctions = ctypes.CDLL(so_file)
c_double_p = ctypes.POINTER(ctypes.c_double)
py_cfunctions.factorial.argtypes = [ctypes.c_int]
py_cfunctions.dotproduct.argtypes= [ctypes.c_int, c_double_p, c_double_p]
py_cfunctions.elementwiseproduct.argtypes = [ctypes.c_int, c_double_p, c_double_p]
And that’s it! You now can import py_cfunctions and call factorial(), dotproduct() and elementwiseproduct().