【问题标题】:SWIG in typemap works, but argout does not类型图中的 SWIG 有效,但 argout 无效
【发布时间】:2014-02-17 20:13:49
【问题描述】:

我有这个文件foobar.h

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

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

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

当然。所以我写了这个 SWIG 类型图:

%typemap(in) int [2] {}

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

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

%typemap(argout) int [2] {}

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

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

我做错了什么???

【问题讨论】:

    标签: python swig


    【解决方案1】:

    怎么了?

    简而言之,这些类型映射不是一个非此即彼的命题。

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

    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"
    

    当 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>
    

    在这些类型映射中,“in”类型映射的目标是在调用之前使arg2 成为一个合理的值,而“argout”类型映射应该对调用后的值做一些合理的事情(可能通过更改返回值,如果你想要)。


    类型映射中应该包含什么?

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

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

    重要的是我们让 SWIG 为我们执行此操作,使用在类型之后添加括号的表示法,而不是将其添加到类型映射的主体中,以便变量的范围是正确的。 (如果我们不这样做,暂时将无法从“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
    }
    

    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
    }
    

    (如果我们愿意,我们可以选择使用Python iterator protocol)。

    如果我们现在编译并运行接口,我们有足够的输入来传递,但还没有返回。在我们编写“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;
    }
    

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

    纯输出参数

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

    我们可以通过修改“in”类型映射来做到这一点,使用numinputs=0 来表示不需要来自 Python 的输入。您还需要在这里适当地初始化临时。现在类型图变得简单了:

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

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

    顺便说一句,您可以通过使用 @ 来避免 SWIG 应用的名称修改(代价是不能在同一个函数上多次使用相同的类型映射,或者使用另一个具有名称冲突的类型映射) “in”类型图中的 987654335@。不过我不建议这样做。

    数组长度不固定?

    最后值得注意的是,我们可以将所有这些类型映射写得更通用,并适用于任何固定大小的数组。为此,我们在类型映射匹配中将 2 更改为“ANY”,然后在类型映射主体中使用 $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"
    

    【讨论】:

    • 哇,这太棒了。我必须到我正在执行此操作的另一台计算机上,然后我将使用您所说的内容。我确实发现自己,将“in”类型映射添加到“argout”类型映射,部分解决了问题 - SWIG 接受参数,尚未正确处理它,但至少初始错误消息不再存在。
    • 你的建议很好,很有启发性,但是,如果我按照你的要求去做,那么我还有另一个问题,我的包装是非法的。请看另一个问题stackoverflow.com/questions/21422148/…
    • 对于最后一个 argout,我想创建一个返回值的元组,所以我添加了 '$result = SWIG_Python_AppendOutput($result, list);' 而不是 '$result = list'。跨度>
    猜你喜欢
    • 1970-01-01
    • 2019-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-29
    • 2020-04-25
    • 2020-04-04
    相关资源
    最近更新 更多