Pass multidimensional array (matrix) to c function using ctypes

Mark Tolonen metolone+gmane at gmail.com
Thu Jan 7 01:25:40 EST 2010


"Daniel Platz" <mail.to.daniel.platz at googlemail.com> wrote in message 
news:63ac1a01-8491-4885-bae7-cb884abb582f at 34g2000yqp.googlegroups.com...
> Hello,
>
> I would like to pass a two dimensional array to C function in a dll. I
> use ctypes to call the function.
>
> I compile the dll with visual studio 2008 express and my C source code
> looks like this.
>
> #include <stdio.h>
> #ifdef __cplusplus
> extern "C" {  // only need to export C interface if
>              // used by C++ source code
> using namespace std;
> #endif
>
>
> __declspec(dllexport) int print(double** ptr, int ny, int nx)
> {
> int i, j;
> for(i=0; i<ny; i++)
> {
> for(j=0; j<nx; j++)
> {
> printf("%.3f \t", *(*(ptr+i)+j));
> }
> printf("\n");
> }
> return 0;
> }
>
> #ifdef __cplusplus
> }
> #endif
>
> As you can see the function expects a doube** variable. I tried to
> call this function from Python with this code.
>
> import ctypes as ct
>
> matrix = ct.cdll.LoadLibrary('matrix.dll')
> getattr(matrix, 'print_matrix')
>
> ptr_type = ct.c_double * 5 * 4
> ptr = ptr_type()
> matrix.print_matrix(ptr, ct.c_int(4), ct.c_int(5))
>
> I expected to see a matrix showing only zeros since I can address the
> single entries in Python by ptr[i][j]. Instead the interpreter returns
> with the error message
>
> WindowsError: exception: access violation reading 0x000000
> WARNING: Failure executing file: <ctypes_matrix.py>
>
> Furthermore, I am wondering if there is a fast way to use a numpy 2D
> array instead or alternatively to cast the ctypes array into a numpy
> array.
>
> Has someone an idea to help me?

A two-dimentional array is not equivalent to a double**, so depending on 
what your requirements are there are two solutions.

1.  To work with your original matrix.c code above, some Python code that 
works is:

-------------------------------------
import ctypes as ct

# I like C types in caps
DOUBLE = ct.c_double
PDOUBLE = ct.POINTER(DOUBLE)
PPDOUBLE = ct.POINTER(PDOUBLE)
INT = ct.c_int

matrix = ct.cdll.LoadLibrary('matrix.dll')
print_matrix = getattr(matrix,'print')

# An array of doubles can be passed to a function that takes double*.
DBL5ARR = DOUBLE * 5
# An array of double* can be passed to your function as double**.
PDBL4ARR = PDOUBLE * 4

# Declare double* array.
ptr = PDBL4ARR()
for i in range(4):
    # fill out each pointer with an array of doubles.
    ptr[i] = DBL5ARR()
    for j in range(5):
        ptr[i][j] = i + j  # just to initialize the actual doubles.

print_matrix(ptr,4,5)
-----------------------------------------------


2.  In C, multidimensional arrays are really just single dimentional arrays 
where the compiler does the math for you, so if you declare your matrix 
print function like this:

----- matrix2.c ---------------------------
#include <stdio.h>
#ifdef __cplusplus
extern "C" {  // only need to export C interface if
              // used by C++ source code
#endif

__declspec(dllexport) int print(double* ptr, int ny, int nx)
{
    int i, j;
    for(i=0; i<ny; i++)
    {
        for(j=0; j<nx; j++)
        {
            printf("%.3f \t", ptr[i*nx + j]);
        }
        printf("\n");
    }
    return 0;
}

#ifdef __cplusplus
}
#endif
-------------------------------------------------

Then the Python code simplifies a bit to:

------------------------------------------
import ctypes as ct

DOUBLE = ct.c_double
PDOUBLE = ct.POINTER(DOUBLE)
INT = ct.c_int

matrix = ct.cdll.LoadLibrary('matrix2.dll')
print_matrix = getattr(matrix,'print')

ptr = (DOUBLE*5*4)()
for i in range(4):
    for j in range(5):
        ptr[i][j] = i + j

print_matrix(ptr,4,5)
----------------------------------------------

OUTPUT in both cases:
0.000   1.000   2.000   3.000   4.000
1.000   2.000   3.000   4.000   5.000
2.000   3.000   4.000   5.000   6.000
3.000   4.000   5.000   6.000   7.000

HTH,
Mark





More information about the Python-list mailing list