SWIG将C库与Python接口(从C“序列”结构创建“可迭代” Python数据类型)

SWIG将C库与Python接口(从C“序列”结构创建“可迭代” Python数据类型),第1张

SWIG将C库与Python接口(从C“序列”结构创建“可迭代” Python数据类型

最简单的解决方案是为无效索引实现

__getitem__
并引发
IndexError
异常。

我整理了一个示例,分别在SWIG中使用

%extend
%exception
来实现
__getitem__
和引发异常:

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

我通过添加到test.h中进行了测试:

static MyStruct *test() {  static MyStruct inst = {0,0};  if (!inst.clientdata) {    inst.len = 10;    inst.clientdata = malloc(sizeof(double)*inst.len);    for (size_t i = 0; i < inst.len; ++i) {      inst.clientdata[i] = i;    }  }  return &inst;}

并运行以下Python:

import testfor i in test.test():  print i

哪些打印:

python run.py0.01.02.03.04.05.06.07.08.09.0

然后完成。


也可以使用一种类型映射来直接映射

MyStruct
到一个
PyList
直接的替代方法:

%module test%{#include "test.h"%}%typemap(out) (MyStruct *) {  PyObject *list = PyList_New(->len);  for (size_t i = 0; i < ->len; ++i) {    PyList_SetItem(list, i, PyFloat_FromDouble(->clientdata[i]));  }  $result = list;}%include "test.h"

这将

PyList
使用任何返回a的函数的返回值创建一个
MyStruct*
。我
%typemap(out)
使用与先前方法完全相同的功能对其进行了测试。

你也可以写一个相应的

%typemap(in)
%typemap(freearg)
为反向,像这样未经测试的代码:

%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;    }  }   = tmp;}%typemap(freearg) (MyStruct *) {  free(->clientdata);  free();}

对于链表之类的容器,使用迭代器更有意义,但是出于完整性考虑,这是您可以

MyStruct
使用的方式
__iter__
。关键点在于,您将获得SWIG来包装另一种类型,它提供了
__iter__()
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;  }}

对容器进行迭代的要求是,容器需要实现

__iter__()
并返回一个新的迭代器,但是除了
next()
返回下一项并增加迭代器之外,迭代器本身还必须提供一个
__iter__()
方法。这意味着容器或迭代器可以相同地使用。

MyStructIter
需要跟踪迭代的当前状态-
我们在哪里以及还剩下多少。在此示例中,我通过保留指向下一个项目的指针和一个用来告诉何时结束的计数器来做到这一点。您还可以通过保持指向
MyStruct
迭代器正在使用的指针以及其中的位置计数器来跟踪状态,例如:

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

(在这种情况下,我们实际上可以将容器本身用作迭代器,作为迭代器,方法是提供一个

__iter__()
返回容器 副本
的容器,并且
next()
类似于第一种类型。我在原始答案中没有这样做,因为我认为这要比具有两种不同的类型(容器和该容器的迭代器)不清楚



欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/5623920.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-12-15
下一篇2022-12-15

发表评论

登录后才能评论

评论列表(0条)

    保存