如何将struct中的numpy数组传递给ctypes函数?

时间:2014-04-23 14:49:37

标签: python numpy ctypes

我正在尝试从我的Python应用程序中的库中包装一些第三方代码。本质上,我想调用的函数将一个结构作为输入,包含(除其他外)指向双数组的指针。一个简化的例子是:

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;
}

生成文件

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

我想使用NumPy,myfuncctypes.Structure包裹numpy.ctypeslib,以便我可以将NumPy数组作为myfunc的属性传递给mystruct_t。到目前为止,我一直在尝试以下方法:

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)

但是,此操作失败并显示以下错误消息:

$ python myfunc.py
Traceback (most recent call last):
  File "./myfunc.py", line 26, in <module>
    mystruct.x = x
TypeError: cannot be converted to pointer

如何正确定义我的功能签名以便进行类型转换?或者我需要以某种方式转换x才能将其分配给mystruct.x

不幸的是我无法更改我想要调用的方法的签名,除非绝对必要,否则我不想编写包装器C代码。我在此处发现的其他问题和资源只涉及ctypes.Structurenumpy.ctypeslib,但您可以让它们协同工作吗?

我上传了缩小的示例as a gist,因此您可以将其作为起点。

非常感谢提前!

1 个答案:

答案 0 :(得分:2)

您可以删除array_1d_double。这不是必需的。

结构应该像这样声明:

class MyStruct(ctypes.Structure):
    _fields_ = [
        ('n', ctypes.c_int),
        ('x', ctypes.POINTER(ctypes.c_double))
    ]

我改变了两种类型。您有c_int16,但您在C代码中使用的类型是int。这映射到c_int。同样对于数组,即double*。在ctypes中POINTER(ctypes.c_double)

结构应该像这样初始化:

mystruct = MyStruct()
mystruct.n = len(x)
mystruct.x = npct.as_ctypes(x)

通过这些更改,您的代码可以正常运行。