类型映射中的SWIG可以工作,但是argout没有

Mar*_*eck 10 python swig

我有这个文件 foobar.h

class Foobar {
public: void method(int arg[2]) {};
};
Run Code Online (Sandbox Code Playgroud)

在将SWIG接口编译为Python之后,如果我尝试从Python运行此方法,它说

TypeError: in method 'Foobar_method', argument 2 of type 'int [2]'
Run Code Online (Sandbox Code Playgroud)

当然.所以我写了这个SWIG类型映射:

%typemap(in) int [2] {}
Run Code Online (Sandbox Code Playgroud)

当我编译它时,Python运行这个方法而不抱怨.所以我想,我理解如何编写一个类型图.

但是,如果我将typemap更改为argout:

%typemap(argout) int [2] {}
Run Code Online (Sandbox Code Playgroud)

现在,Python回到了之前的错误.

我只是直接从SWIG手册中做到这一点,这应该没有那个错误,就像intypemap 一样.

我究竟做错了什么???

Fle*_*exo 19

怎么了?

简而言之,它不是这些类型映射的两者之一.

您缺少的关键信息是多个类型映射协作包装单个函数的方式.

argout调用发生,会在生成的包装器中插入.这是您以合理的方式将(现在已修改的)输入复制回Python的机会.

这并没有解决在调用之前如何创建和传入参数的问题.

通过检查此接口生成的代码,您可以非常清楚地看到这一点:

%module test

%{
#include "test.h"
%}

%typemap(in) int[2] {
  // "In" typemap goes here
}

%typemap(argout) int[2] {
  // "argout" goes here
}

%include "test.h"
Run Code Online (Sandbox Code Playgroud)

哪个,当test.h是你的例子时产生:

  // ... <snip>
  arg1 = reinterpret_cast< Foobar * >(argp1);
  {
    // "In" typemap goes here
  }
  (arg1)->method(arg2);
  resultobj = SWIG_Py_Void();
  {
    // "argout" goes here
  }
  return resultobj;
  // ... <snip>
Run Code Online (Sandbox Code Playgroud)

在这些类型映射中,"in"类型映射的目标是arg2在调用之前创建一个合理的值,并且"argout"类型映射应该对调用后的值做一些合理的事情(如果需要,可以通过更改返回值).


字体图中应该包含哪些内容?

通常对于类似的函数,您可能希望输入类型映射从某些Python输入填充临时数组.

为此,我们首先需要更改输入类型映射,要求SWIG为我们创建一个临时数组:

重要的是我们让SWIG为我们这样做,使用在类型后添加括号的符号,而不是将其添加到typemap的主体内,以便范围对于变量是正确的.(如果我们没有,那么暂时不会从"argout"类型映射中访问,并且在调用本身之前将被清除).

%typemap(in) int[2] (int temp[2]) {
  // If we defined the temporary here then it would be out of scope too early.
  // "In" typemap goes here
}
Run Code Online (Sandbox Code Playgroud)

SWIG生成的代码现在包含了我们的临时数组,因此我们希望使用Python C API来迭代输入.这可能看起来像:

%typemap(in) int[2] (int temp[2]) {
  // "In" typemap goes here:
  for (Py_ssize_t i = 0; i < PyList_Size($input); ++i) {
    assert(i < sizeof temp/sizeof *temp); // Do something smarter
    temp[i] = PyInt_AsLong(PyList_GetItem($input, i)); // Handle errors
  }
  $1 = temp; // Use the temporary as our input
}
Run Code Online (Sandbox Code Playgroud)

(如果我们愿意,我们可以选择使用Python迭代器协议).

如果我们现在编译并运行接口,我们有足够的传递输入,但还没有回来.在我们编写"argout"类型映射之前,在生成的代码中还有一件事需要注意.生成的代码中的临时数组实际上看起来像int temp2[2].这不是一个错误,SWIG默认将变量重命名为从参数位置派生,以允许同一个类型映射多次应用于单个函数调用,如果需要,每个参数一次.

在我的"argout"类型映射中,我将返回另一个包含新值的Python列表.这不是唯一明智的选择 - 如果您愿意,还有其他选择.

%typemap(argout) int[2] {
  // "argout" goes here:
  PyObject *list = PyList_New(2);
  for (size_t i = 0; i < 2; ++i) {
    PyList_SetItem(list, i, PyInt_FromLong(temp$argnum[i]));
  }
  $result = list;
}
Run Code Online (Sandbox Code Playgroud)

这里的两个注意事项首先是我们需要temp$argnum明确地编写以匹配SWIG在我们的临时数组上进行的转换,其次是我们$result用作输出.

纯粹输出参数

通常我们有一个仅用于输出而不是输入的参数.对于这些,强制Python用户提供一个将被忽略的列表是没有意义的.

我们可以通过修改"in"类型映射来做到这一点,numinputs=0用来指示Python不需要输入.你也需要在这里适当地初始化临时.现在,typemap变得简单:

%typemap(in,numinputs=0) int[2] (int temp[2]) {
  // "In" typemap goes here:
  memset(temp, 0, sizeof temp);
  $1 = temp;
}
Run Code Online (Sandbox Code Playgroud)

所以现在"in"typemap实际上根本不接受Python的任何输入.它可以被视为简单地准备本机调用的输入.

通过旁白,您可以避免SWIG应用的名称修改(在同一个函数上无法多次使用相同的类型映射,或者使用另一个具有名称冲突的类型映射),可以使用noblock=1"in" "typemap.我不建议这样做.

非固定阵列长度?

最后值得注意的是,我们可以将所有这些类型映射编写为更通用,并适用于任何固定大小的数组.为此,我们在typemap匹配中将2更改为"ANY",然后在typemap实体中使用$1_dim0而不是2,因此最后的整个界面变为:

%module test

%{
#include "test.h"
%}

%typemap(in,numinputs=0) int[ANY] (int temp[$1_dim0]) {
  // "In" typemap goes here:
  memset(temp, 0, sizeof temp);
  $1 = temp;
}

%typemap(argout) int[ANY] {
  // "argout" goes here:
  PyObject *list = PyList_New($1_dim0);
  for (size_t i = 0; i < $1_dim0; ++i) {
    PyList_SetItem(list, i, PyInt_FromLong(temp$argnum[i]));
  }
  $result = list;
}

%include "test.h"
Run Code Online (Sandbox Code Playgroud)