将C库与Python接口的SWIG(从C';序列';结构创建&';Python数据类型)
我已经为C库编写了Python扩展。我的数据结构如下所示:将C库与Python接口的SWIG(从C';序列';结构创建&';Python数据类型),python,c,swig,Python,C,Swig,我已经为C库编写了Python扩展。我的数据结构如下所示: typedef struct _mystruct{ double * clientdata; size_t len; } MyStruct; 此数据类型的用途直接映射到Python中的列表数据类型。因此,我希望为导出的结构创建“类似列表”的行为,以便使用我的C扩展编写的代码更“Pythonic” 特别是,这是我希望能够做到的(来自python代码) 注意:py_ctsruct是在python中访问的ctsruct数据类
typedef struct _mystruct{
double * clientdata;
size_t len;
} MyStruct;
此数据类型的用途直接映射到Python中的列表数据类型。因此,我希望为导出的结构创建“类似列表”的行为,以便使用我的C扩展编写的代码更“Pythonic”
特别是,这是我希望能够做到的(来自python代码)
注意:py_ctsruct是在python中访问的ctsruct数据类型
我的要求可以概括为:
for i in py_cstruct:
print i
Python进入一个无限循环,在控制台上只打印上面提到的神奇(错误)数字。这对我来说意味着索引有问题
最后但并非最不重要的一点,我如何实现需求1?这包括(据我所知):
- 处理python中的函数调用list()
- 从C代码返回Python(列表)数据类型
对于我正在处理的一个接口,我使用了一个类对象,它有一些方法来访问代码中的数据。这些方法是用C++编写的。然后,在“I”文件内部的类中使用了%python代码指令,在Python代码中创建了“<强> GestITE<强>”和“<强> SeTiTEM <强> >方法,使用EnguleC++方法使其看起来像字典式访问。 您说您还没有实现Python异常抛出-这就是问题所在。从政治公众人物234: 定义了一个新的异常StopIteration,它可以用来发出迭代结束的信号 您必须在迭代结束时设置此异常。由于您的代码没有做到这一点,因此您遇到了您描述的情况:
iternext
函数中循环StopIteration
异常,而是简单地返回您的“幻数”iternext
返回的值。。。你的神奇数字。对于口译员来说,这只是另一个列表成员PyErr_occurrent()
的输出以查看是否设置了错误,以及是否设置了错误,打印相关的异常和回溯
因此,在函数中,当到达数组末尾时,您只需要:
PyErr_SetString(PyExc_StopIteration,"End of list");
return NULL;
下面是关于这个问题的另一个很好的答案:解决这个问题的最简单方法是实现一个无效索引并抛出一个异常 我举了一个例子,在SWIG中使用
%extend
和%exception
分别实现\uu getitem\uuuuuuuuu
和引发异常:
%module test
%include "exception.i"
%{
#include <assert.h>
#include "test.h"
static int myErr = 0; // flag to save error state
%}
%exception MyStruct::__getitem__ {
assert(!myErr);
$action
if (myErr) {
myErr = 0; // clear flag for next time
// You could also check the value in $result, but it's a PyObject here
SWIG_exception(SWIG_IndexError, "Index out of bounds");
}
}
%include "test.h"
%extend MyStruct {
double __getitem__(size_t i) {
if (i >= $self->len) {
myErr = 1;
return 0;
}
return $self->clientdata[i];
}
}
其中打印:
python run.py
0.0
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
然后结束
另一种方法是使用typemap将
MyStruct
直接映射到PyList
上:
%module test
%{
#include "test.h"
%}
%typemap(out) (MyStruct *) {
PyObject *list = PyList_New($1->len);
for (size_t i = 0; i < $1->len; ++i) {
PyList_SetItem(list, i, PyFloat_FromDouble($1->clientdata[i]));
}
$result = list;
}
%include "test.h"
对于像链表这样的容器,使用迭代器更有意义,但为了完整性起见,下面是使用
\uu iter\uu
为MyStruct
执行迭代器的方法。关键是让SWIG为您包装另一种类型,它提供了所需的\uuuu iter\uuuu()
和next()
,在这种情况下MyStructIter
使用%inline
同时定义和包装,因为它不是普通C API的一部分:
%module test
%include "exception.i"
%{
#include <assert.h>
#include "test.h"
static int myErr = 0;
%}
%exception MyStructIter::next {
assert(!myErr);
$action
if (myErr) {
myErr = 0; // clear flag for next time
PyErr_SetString(PyExc_StopIteration, "End of iterator");
return NULL;
}
}
%inline %{
struct MyStructIter {
double *ptr;
size_t len;
};
%}
%include "test.h"
%extend MyStructIter {
struct MyStructIter *__iter__() {
return $self;
}
double next() {
if ($self->len--) {
return *$self->ptr++;
}
myErr = 1;
return 0;
}
}
%extend MyStruct {
struct MyStructIter __iter__() {
struct MyStructIter ret = { $self->clientdata, $self->len };
return ret;
}
}
(在本例中,我们实际上可以使用容器本身作为迭代器作为迭代器,方法是提供一个\uuu iter\uuuu()
,返回容器的副本和一个next()
python run.py
0.0
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
%module test
%{
#include "test.h"
%}
%typemap(out) (MyStruct *) {
PyObject *list = PyList_New($1->len);
for (size_t i = 0; i < $1->len; ++i) {
PyList_SetItem(list, i, PyFloat_FromDouble($1->clientdata[i]));
}
$result = list;
}
%include "test.h"
%typemap(in) (MyStruct *) {
if (!PyList_Check($input)) {
SWIG_exception(SWIG_TypeError, "Expecting a PyList");
return NULL;
}
MyStruct *tmp = malloc(sizeof(MyStruct));
tmp->len = PyList_Size($input);
tmp->clientdata = malloc(sizeof(double) * tmp->len);
for (size_t i = 0; i < tmp->len; ++i) {
tmp->clientdata[i] = PyFloat_AsDouble(PyList_GetItem($input, i));
if (PyErr_Occured()) {
free(tmp->clientdata);
free(tmp);
SWIG_exception(SWIG_TypeError, "Expecting a double");
return NULL;
}
}
$1 = tmp;
}
%typemap(freearg) (MyStruct *) {
free($1->clientdata);
free($1);
}
%module test
%include "exception.i"
%{
#include <assert.h>
#include "test.h"
static int myErr = 0;
%}
%exception MyStructIter::next {
assert(!myErr);
$action
if (myErr) {
myErr = 0; // clear flag for next time
PyErr_SetString(PyExc_StopIteration, "End of iterator");
return NULL;
}
}
%inline %{
struct MyStructIter {
double *ptr;
size_t len;
};
%}
%include "test.h"
%extend MyStructIter {
struct MyStructIter *__iter__() {
return $self;
}
double next() {
if ($self->len--) {
return *$self->ptr++;
}
myErr = 1;
return 0;
}
}
%extend MyStruct {
struct MyStructIter __iter__() {
struct MyStructIter ret = { $self->clientdata, $self->len };
return ret;
}
}
%inline %{
struct MyStructIter {
MyStruct *list;
size_t pos;
};
%}
%include "test.h"
%extend MyStructIter {
struct MyStructIter *__iter__() {
return $self;
}
double next() {
if ($self->pos < $self->list->len) {
return $self->list->clientdata[$self->pos++];
}
myErr = 1;
return 0;
}
}
%extend MyStruct {
struct MyStructIter __iter__() {
struct MyStructIter ret = { $self, 0 };
return ret;
}
}
%ignore MyStructIter::atEnd();
%exception MyStructIter::next {
if( $self->list->atEnd() ) {
PyErr_SetString(PyExc_StopIteration,"End of list");
SWIG_fail;
}
$action
}
%exception MyStructIter::next {
if( $self->pos >= $self->list->len ) {
PyErr_SetString(PyExc_StopIteration,"End of list");
SWIG_fail;
}
$action
}
%rename(__next__) MyStructIter::next;