类型映射中的 SWIG 有效,但 argout 无效 [英] SWIG in typemap works, but argout does not

查看:21
本文介绍了类型映射中的 SWIG 有效,但 argout 无效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个文件 foobar.h

class Foobar {
public: void method(int arg[2]) {};
};

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

After compiling SWIG interface to Python, if I try to run this method from Python it says

TypeError: in method 'Foobar_method', argument 2 of type 'int [2]'

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

Certainly. So I write this SWIG typemap:

%typemap(in) int [2] {}

当我编译它时,Python 会毫无怨言地运行这个方法.所以我想,我了解如何编写类型映射.

and when I compile this, Python runs this method without complaining. So I think, I understand how to write a typemap.

但是,如果我将类型映射更改为 argout:

But, if I change the typemap to argout:

%typemap(argout) int [2] {}

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

Now, Python goes back to the previous error.

我只是直接从 SWIG 手册中执行此操作,这应该可以正常工作而不会出现该错误,就像 in 类型映射一样.

I just do this directly from the SWIG manual, this should work without that error, just like in typemap.

我做错了什么???

推荐答案

怎么了?

简而言之,这些类型图不是非此即彼的命题.

What's wrong?

In short it's not an either/or proposition with these typemaps.

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

The key bit of information you're missing is the way multiple typemaps cooperate to wrap a single function.

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

argout gets inserted in the generated wrapper after the call has happened. It's your opportunity to copy the (now modified) input back to Python in a sensible way.

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

That doesn't address the issue of how the argument gets created and passed in before the call however.

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

You can see this quite clearly by inspecting the code generated by this interface:

%module test

%{
#include "test.h"
%}

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

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

%include "test.h"

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

Which, when test.h is your example produces:

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

在那些类型映射中,in"类型映射的目标是在调用之前使 arg2 成为一个合理的值,而argout"类型映射应该对调用后的值做一些合理的处理(可能通过如果需要,可以更改返回值).

In those typemaps the goal of the "in" typemap is to make arg2 a sensible value before the call and the "argout" typemap should do something sensible with the values after the call (possibly by changing the return value if you want).

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

Typically for a function like that you might want to have the input typemap populate a temporary array from some Python inputs.

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

To do that we're going to need to change the input typemap first, asking SWIG to create a temporary array for us:

让 SWIG 为我们做这件事很重要,使用在类型后添加括号的表示法,而不是将它添加到类型映射的主体内,以便变量的作用域是正确的.(如果我们不这样做,临时文件仍然无法从argout"类型映射中访问,并且会在调用本身之前被清除).

It's important that we get SWIG to do this for us, using the notation of adding parenthesis after the type instead of adding it inside the body of the typemap so that the scope is correct for the variable. (If we didn't the temporary wouldn't be accessible from the "argout" typemap still and would be cleaned up before the call itself was made even).

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

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

The code generated by SWIG now includes that temporary array for us, so we want to use the Python C API to iterate over our input. That might look something like:

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

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

(We could have chosen to use Python iterator protocol instead if we preferred).

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

If we compile and run the interface now we have enough to pass in an input, but nothing comes back yet. Before we write the "argout" typemap there's one thing still to notice in the generated code. Our temporary array in the generated code actually looks like int temp2[2]. That's not a mistake, SWIG has by default renamed the variable to be derived from the argument position in order to permit the same typemap to be applied multiple times to a single function call, once per argument if needed.

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

In my "argout" typemap I'm going to return another Python list with the new values. This isn't the only sane choice by a long way though - there are other options if you prefer.

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

这里需要注意的两点首先是我们需要显式地编写 temp$argnum 以匹配 SWIG 对临时数组所做的转换,其次我们正在使用 $结果 作为输出.

The two points of note in this are firstly that we need to write temp$argnum explicitly to match the transformation that SWIG did on our temporary array and secondly that we're using $result as the output.

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

Often we have an argument that is just used for output, not input. For these it makes no sense to force the Python user to supply a list that's just going to be ignored.

我们可以通过修改in"类型映射来做到这一点,使用 numinputs=0 表示不需要来自 Python 的输入.您还需要注意在此处适当地初始化临时文件.类型映射现在变得很简单:

We can do that by modifying the "in" typemap, using numinputs=0 to indicate that no input is expected from Python. You'll need to take care of initializing the temporary appropriately here too. The typemap now becomes simply:

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

所以现在in"类型映射实际上根本不接受来自 Python 的任何输入.可以将其视为简单地准备本地调用的输入.

So now the "in" typemap doesn't actually take any input from Python at all. It can be seen as simply preparing the input to the native call.

顺便说一句,您可以使用 noblock=1 在in"类型映射中.不过我不建议这样做.

By way of an aside you can avoid the name mangling that SWIG applies (with the cost of not being able to use the same typemap multiple times on the same function, or with another typemap that has a name clash) by using noblock=1 in the "in" typemap. I wouldn't recommend that though.

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

Finally it's worth noting that we can write all of these typemaps to be more generic and work for any, fixed, size array. To do that we change 2 to "ANY" in the typemap matching and then use $1_dim0 instead of 2 inside the typemap bodies, so the whole interface at the end of that becomes:

%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"

这篇关于类型映射中的 SWIG 有效,但 argout 无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆