我想从 python 访问一个 C 函数,该函数返回一个包含双精度数组的结构(其中这些数组的长度由该结构的其他 int 成员给出)。声明是
typedef struct {
int dim;
int vertices;
int quadrature_degree;
int polynomial_degree;
int ngi;
int quadrature_familiy;
double *weight; /* 1D: ngi */
double *l; /* 2D: ngi * dim */
double *n; /* 2D: ngi * vertices */
double *dn; /* 3D: ngi * vertices * dim */
} element;
extern void get_element(int dim, int vertices, int quad_degree, int poly_degree, element* e);
重要的一点是我希望能够将所有 double*
成员作为正确形状的 NumPy 数组访问(即 dn
应该是可访问的 3D 数组) )。
简单地 SWIG 包装这给了我很好的结构,但是所有 double*
成员都是
的对象,这使得它们毫无用处。我尝试了 NumPy SWIG 接口文件,但无法获得任何类型映射,例如 ( DATA_TYPE* INPLACE_ARRAY1, int DIM1 )
来工作(我认为在这种情况下不可能让它们匹配)但我很高兴被证明是错误的)。
我的猜测是,我必须将 NumPy 数组的代码初始化为这些成员的 PyArrayObject
,并且 SWIG 扩展我的结构以使其可以在 Python 中访问?看起来工作量很大。任何人都可以看到使用 SWIG 的更好方法吗?如果可以使事情变得更容易,则可以更改结构或返回它的方法。
另外,我查看了 cython 和 ctypes。这些是否更适合我想要实现的目标?我没有使用过 cython 所以无法判断它的包装能力。对于 ctypes,我可以粗略地想象如何做到这一点,但这意味着手动编写我希望一个相当自动化的包装器可以为我做的事情。
如有任何建议,不胜感激!
I want to access a C function that returns a struct containing double arrays (where the lengths of these arrays is given by other int members of the struct) from python. The declaration is
typedef struct {
int dim;
int vertices;
int quadrature_degree;
int polynomial_degree;
int ngi;
int quadrature_familiy;
double *weight; /* 1D: ngi */
double *l; /* 2D: ngi * dim */
double *n; /* 2D: ngi * vertices */
double *dn; /* 3D: ngi * vertices * dim */
} element;
extern void get_element(int dim, int vertices, int quad_degree, int poly_degree, element* e);
The important point is I want to be able to access all the double*
members as NumPy arrays of the correct shape (i.e. dn
should be a accessible as 3D array).
Simply SWIG-wrapping this gives me the struct just fine, but all the double*
members are <Swig Object of type 'double *' at 0x348c8a0>
which makes them useless. I played around with the NumPy SWIG interface file but couldn't get any of the typemaps like ( DATA_TYPE* INPLACE_ARRAY1, int DIM1 )
to work (I think it's not possible to get them to match in this case but I'd be happy to be proven wrong).
My guess is I'd have to hand code initialization of the NumPy arrays as PyArrayObject
for these members and SWIG extend my struct to make them accessible in Python? That looks like a lot of work. Can anyone see a nicer way using SWIG? It would be possible to change the struct or the method returning it if that made things easier.
Alternatively I had a look at cython and ctypes. Would these be better suited for what I'm trying to achieve? I haven't used cython so can't judge it's wrapping capabilities. For ctypes I can roughly imagine how to do it, but it means writing by hand what I had hoped a reasonably automated wrapper could do for me.
Any suggestions gratefully received!
发布评论
评论(5)
Cython 规则:
然后你可以从 python 空间连接它
Cython rules:
and then you can interface it, from python space
使用 SWIG 需要整个结构的类型映射。仅指针成员的类型映射是不够的,因为它们没有上下文来知道初始化 NumPy 数组的大小。我设法通过以下类型映射获得了我想要的东西(基本上是从 numpy.i 复制和粘贴并适应我的需求,可能不是很强大):
这与 C 函数的工作方式不同,因为它返回 NumPy 的元组包含我想要的数据的数组,这比稍后从
element
对象中提取它更方便。第一个类型映射还消除了传入element
类型的对象的需要。因此,我可以对 python 用户完全隐藏element
结构。python的界面最终是这样的:
Using SWIG requires a typemap for the entire struct. Tyepmaps for only the pointer members are not enough, since they don't have the context to know what size to initialize the NumPy arrays with. I managed to get what I wanted with the following typemaps (which was basically copy & paste from numpy.i and adapt to my needs, probably not very robust):
This works different from the C function in that it returns a tuple of NumPy arrays with the data I want, which is more convenient than having to extract it from the
element
object later. The first typemap furthermore eliminates the need to pass in an object of typeelement
. Hence I can hide theelement
struct entirely from the python user.The python interface finally looks like this:
查看 SWIG 的类型映射。它们允许您编写自己的代码来处理特定类型、特定实例(类型+名称)甚至参数组。我没有对结构这样做,而是专门处理 C 函数采用数组及其大小的情况:
这将采用参数对
int argc, Descriptor* argv
(因为名称前提是它们也必须匹配)并向您传递所使用的 PyObject,然后您编写进行转换所需的任何 C 代码。您可以为double *dn
执行类型映射,使用 NumPy C API 进行转换。Check out SWIG's typemaps. They let you write your own code for handling specific types, specific instances (type+name) or even groups of arguments. I haven't done it for structures, but to specially handle a case where the C function takes an array and its size:
That will take the pair of arguments
int argc, Descriptor* argv
(since the names are provided they have to match as well) and pass you the PyObject used and you write whatever C code you need to do the conversion. You could do a typemap fordouble *dn
that would use the NumPy C API to do the conversion.您始终可以编写采用“元素 *”并返回您要查找的元素的辅助函数:
如果您需要修改和读取,当然,您需要单独的“getters”和“setters”。
SWIG 应该能够轻松包装所有这些并将它们公开给 Python。
性能可能不是很好,但可能不比其他方案差。
You could always write helper functions that take an "element *" and return the element you seek:
If you need to modify as well as read, you will want separate "getters" and "setters", of course.
SWIG should be able to wrap all of these easily and expose them to Python.
Performance might not be great, but probably no worse than the alternatives.
使用
ctypes
创建的 SWIG 模块的等效项如下所示:An equivalent to the SWIG created module using
ctypes
looks as follows: