Pass a 2d numpy array to c using ctypes

jrsm picture jrsm · Mar 15, 2014 · Viewed 12.4k times · Source

What is the correct way to pass a numpy 2d - array to a c function using ctypes ? My current approach so far (leads to a segfault):

C code :

void test(double **in_array, int N) {
    int i, j;
    for(i = 0; i<N; i++) {
        for(j = 0; j<N; j++) {
            printf("%e \t", in_array[i][j]);
        }
        printf("\n");
    }
}

Python code:

from ctypes import *
import numpy.ctypeslib as npct

array_2d_double = npct.ndpointer(dtype=np.double,ndim=2, flags='CONTIGUOUS')
liblr = npct.load_library('libtest.so', './src')

liblr.test.restype = None
liblr.test.argtypes = [array_2d_double, c_int]

x = np.arange(100).reshape((10,10)).astype(np.double)
liblr.test(x, 10)

Answer

Yuxiang Wang picture Yuxiang Wang · Jan 2, 2015

This is probably a late answer, but I finally got it working. All credit goes to Sturla Molden at this link.

The key is, note that double** is an array of type np.uintp. Therefore, we have

xpp = (x.ctypes.data + np.arange(x.shape[0]) * x.strides[0]).astype(np.uintp)
doublepp = np.ctypeslib.ndpointer(dtype=np.uintp)

And then use doublepp as the type, pass xpp in. See full code attached.

The C code:

// dummy.c 
#include <stdlib.h> 

__declspec(dllexport) void foobar(const int m, const int n, const 
double **x, double **y) 
{ 
    size_t i, j; 
    for(i=0; i<m; i++) 
        for(j=0; j<n; j++) 
            y[i][j] = x[i][j]; 
} 

The Python code:

# test.py 
import numpy as np 
from numpy.ctypeslib import ndpointer 
import ctypes 

_doublepp = ndpointer(dtype=np.uintp, ndim=1, flags='C') 

_dll = ctypes.CDLL('dummy.dll') 

_foobar = _dll.foobar 
_foobar.argtypes = [ctypes.c_int, ctypes.c_int, _doublepp, _doublepp] 
_foobar.restype = None 

def foobar(x): 
    y = np.zeros_like(x) 
    xpp = (x.__array_interface__['data'][0] 
      + np.arange(x.shape[0])*x.strides[0]).astype(np.uintp) 
    ypp = (y.__array_interface__['data'][0] 
      + np.arange(y.shape[0])*y.strides[0]).astype(np.uintp) 
    m = ctypes.c_int(x.shape[0]) 
    n = ctypes.c_int(x.shape[1]) 
    _foobar(m, n, xpp, ypp) 
    return y 

if __name__ == '__main__': 
    x = np.arange(9.).reshape((3, 3)) 
    y = foobar(x) 

Hope it helps,

Shawn