68

Oh my word I'm a fool. I was simply omitting the second and third arguments when calling the function. Like a fool. Because that's what I am. Original silly question follows:

This seems like it must be a very common thing to do, but I can't find a relevant tutorial, and I'm too ignorant about Numpy and ctypes to figure it out myself.

I have a C function in file ctest.c.

#include <stdio.h>

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] * 2;
    }
    puts("Done!");
}

(As you may guess, I originally had the arguments as double * rather than void *, but couldn't figure out what to do on the Python side. I'd certainly love to change them back, but I'm not picky as long as it works.)

I make a shared library out of it. gcc -fPIC -shared -o ctest.so ctest.c

Then in Python, I have a couple numpy arrays, and I'd like to pass them to the C function, one as input and one as output.

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))

print 'indata: %s' % indata
print 'outdata: %s' % outdata

This doesn't report any errors, but prints out

>>> Here we go!
Done!
indata: [[ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]]
outdata: [[ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]]

The outdata array is not modified. And in fact if I call the function again I get a segfault. Which doesn't surprise me -- I really don't know what I'm doing here. Can anyone point me in the right direction?

2
  • I'm not calling the function in C. Do I need to? Commented May 2, 2011 at 22:09
  • Sorry. It seems you are mixing C and python and I don't know how it works. The fact that the puts in C is called tells it is being called from python code. But I suspect what you are passing to the C function is causing the problems. Commented May 2, 2011 at 22:15

2 Answers 2

88

While not a direct answer to your original question, here's a much more convenient way to call your function. First, make the prototype of your C function exactly as you would do it in plain C. Since you don't need rowcount and colcount separately, I'll collapse them into a single size parameter:

void cfun(const double *indatav, size_t size, double *outdatav) 
{
    size_t i;
    for (i = 0; i < size; ++i)
        outdatav[i] = indatav[i] * 2.0;
}

Now define the ctypes prototype in the following way:

import ctypes
from numpy.ctypeslib import ndpointer
lib = ctypes.cdll.LoadLibrary("./ctest.so")
fun = lib.cfun
fun.restype = None
fun.argtypes = [ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),
                ctypes.c_size_t,
                ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")]

Now, calls to your function will be really convenient:

indata = numpy.ones((5,6))
outdata = numpy.empty((5,6))
fun(indata, indata.size, outdata)

You could also define a wrapper to make this even more convenient:

def wrap_fun(indata, outdata):
    assert indata.size == outdata.size
    fun(indata, indata.size, outdata)
Sign up to request clarification or add additional context in comments.

12 Comments

you might need numpy.ascontiguousarray() if the arrays are non-contiguous e.g., numpy.arange(1, 7)[::2].
@J.F.Sebastian: You can add flags="C_CONTIGUOUS" to the ndpointer() calls to dynamically check whether the array is C-contiguous. (Maybe I should add this to this answer.)
flags parameter leads to TypeError if input array is not contiguous.
@J.F.Sebastian: That's what I tried to imply by saying that it adds dynamic type checks.
Do you consider TypeError to be the correct answer?
|
22

Just pass all four arguments to the C function. Change your Python code from:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

To:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.