Вопрос

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.Structures 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!

Это было полезно?

Решение

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top