Reputation: 2067
I'm trying to wrap some third-party code from a library in my Python application. Essentially, the function I would like to call takes a struct as input that houses (among other things) pointers to double arrays. A simplified example would be:
myfunc.h:
typedef struct mystruct_t {
int n;
double *x;
} mystruct_t;
double myfunc(mystruct_t* data);
myfunc.c:
#include "myfunc.h"
double myfunc(mystruct_t* data) {
double result = 0.;
for(int i = 0; i < data->n; i++) {
result += data->x[i];
}
return result;
}
Makefile:
CC = gcc
CFLAGS = -g -Wall -fPIC -lm -std=gnu99
all: libmyfunc.so
m.PHONY : clean
libmyfunc.so: myfunc.o
gcc -shared -Wl,-soname,$@ -o $@ $^
%.o: %.c
$(CC) -c $(CFLAGS) $<
clean:
rm -vf libmyfunc.so *.o
I would like to wrap myfunc
using NumPy, ctypes.Structure
and numpy.ctypeslib
so that I can pass NumPy arrays to myfunc
as attributes of mystruct_t
. So far I've been trying the following:
myfunc.py:
#!/usr/bin/env python
import numpy as np
import numpy.ctypeslib as npct
import ctypes
import os
array_1d_double = npct.ndpointer(dtype=np.double, ndim=1, flags='C_CONTIGUOUS')
class MyStruct(ctypes.Structure):
_fields_ = [
('n', ctypes.c_int16),
('x', array_1d_double)
]
libmyfunc = npct.load_library('libmyfunc', os.path.dirname(__file__))
libmyfunc.myfunc.restype = ctypes.c_double
libmyfunc.myfunc.argtypes = [
ctypes.POINTER(MyStruct)
]
x = np.array([1.0, 2.0, 3.0, 4.0])
mystruct = MyStruct()
mystruct.n = len(x)
mystruct.x = x
res = libmyfunc.myfunc(mystruct)
However, this fails with the following error message:
$ python myfunc.py
Traceback (most recent call last):
File "./myfunc.py", line 26, in <module>
mystruct.x = x
TypeError: cannot be converted to pointer
How do I define my function signatures correctly so that type conversion can happen? Or do I need to convert x
somehow before assigning it to mystruct.x
?
Unfortunately I cannot change the signature of the method I would like to call and I would like to not have to write wrapper C code unless it is absolutely necessary. The other questions and resources that I've found on this only deal with either ctypes.Structure
s or numpy.ctypeslib
, but can you make them work together?
I've uploaded my reduced example as a gist so you can use it as a starting point.
Many thanks in advance!
Upvotes: 3
Views: 1589
Reputation: 612964
You can remove array_1d_double
. It is not needed.
The struct should be declared like this:
class MyStruct(ctypes.Structure):
_fields_ = [
('n', ctypes.c_int),
('x', ctypes.POINTER(ctypes.c_double))
]
I've changed both types. You had c_int16
, but the type you used in the C code is int
. That maps to c_int
. And likewise for the array, that is double*
. In ctypes that is POINTER(ctypes.c_double)
.
The struct should be initialised like this:
mystruct = MyStruct()
mystruct.n = len(x)
mystruct.x = npct.as_ctypes(x)
With these changes, your code works as intended.
Upvotes: 4