9

I could pass one-dimension array to python like below. and I wonder if I can pass c++ double pointer array to python by using ctypes, numpy.

test.cpp:

#include <stdio.h>
extern "C" void cfun(const void * indatav, int rowcount, int colcount, void * outdatav);

void cfun(const void * indatav, int rowcount, int colcount, void * outdatav) {
    //void cfun(const double * indata, int rowcount, int colcount, double * outdata) {
    const double * indata = (double *) indatav;
    double * outdata = (double *) outdatav;
    int i;
    puts("Here we go!");
    for (i = 0; i < rowcount * colcount; ++i) {
        outdata[i] = indata[i] * 4;
    }
    puts("Done!");
}

test.py:

import numpy
import ctypes

indata = numpy.ones((5,6), dtype=numpy.double)
outdata = numpy.zeros((5,6), dtype=numpy.double)
lib = ctypes.cdll.LoadLibrary('./ctest.so')
fun = lib.cfun
# Here comes the fool part.
#fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_int(5), ctypes.c_int(6),
    ctypes.c_void_p(outdata.ctypes.data))


print 'indata: %s' % indata
print 'outdata: %s' % outdata
wonjun
  • 311
  • 2
  • 10

1 Answers1

8

Here's a way. I didn't see a nice way to use numpy with double**.

test.cpp (Windows)

#include <stdio.h>

extern "C" __declspec(dllexport) void cfun(const double ** indata, int rowcount, int colcount, double ** outdata) {
    for (int i = 0; i < rowcount; ++i) {
        for (int j = 0; j < colcount; ++j) {
            outdata[i][j] = indata[i][j] * 4;
        }
    }
}

test.py

import numpy
import ctypes

# Allocate array of double*
indata = (ctypes.POINTER(ctypes.c_double) * 5)()
for i in range(5):
    # Allocate arrays of double
    indata[i] = (ctypes.c_double * 6)()
    for j in range(6):
        indata[i][j] = 1.0

outdata = (ctypes.POINTER(ctypes.c_double) * 5)()
for i in range(5):
    outdata[i] = (ctypes.c_double * 6)()
    for j in range(6):
        outdata[i][j] = 1.0

lib = ctypes.cdll.LoadLibrary('test')
fun = lib.cfun

def dump(a,rows,cols):
    for i in range(rows):
        for j in range(cols):
            print a[i][j],
        print

dump(indata,5,6)
fun(ctypes.byref(indata),5,6,ctypes.byref(outdata))
dump(outdata,5,6)

Output

1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0
4.0 4.0 4.0 4.0 4.0 4.0
4.0 4.0 4.0 4.0 4.0 4.0
4.0 4.0 4.0 4.0 4.0 4.0
4.0 4.0 4.0 4.0 4.0 4.0
4.0 4.0 4.0 4.0 4.0 4.0
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • I got an error while creating ".so" file test.cpp:4:1: error: expected constructor, destructor, or type conversion before ‘(’ token – wonjun Jan 09 '12 at 05:22
  • it works fine without "__declspec(dllexport)". thanks and in fortran, multi-dimensional array can be passed directly with numpy right? fwrap.. actually I am looking for passing array among python <-> c,c++ <-> fortran. – wonjun Jan 09 '12 at 05:51
  • Yes, `__declspec(dllexport)` is Windows-specific to export a function from a dynamic library. The code you posted works as is (with minor mods for Windows, for me) with a double pointer and multi-dimensional array. You don't need all that casting, though. What exactly is your question? – Mark Tolonen Jan 09 '12 at 06:57
  • I haven't touched Fortran for years so can't help there. I'll note that if you write your original C++ function with `double*` instead of `void*` you can pass `_____.ctypes.data` directly without the `ctypes.c_void_p` wrappers. You also can pass `5,6` instead of `ctypes.c_int(5),ctypes.c_int(6)`. – Mark Tolonen Jan 09 '12 at 07:11
  • Is there a way to pass this array by using numpy? I want to use numpy not only using ctypes. – wonjun Jan 09 '12 at 08:51
  • Not that I know of if you are forced to call an existing function that takes double**. You could write a conversion function to convert from a multi-dimensional numpy array to double**. – Mark Tolonen Jan 09 '12 at 15:36