我有这个文件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
typemap一样。
我做错了什么???
答案 0 :(得分:18)
简而言之,它不是这些类型映射的两者之一。
您缺少的信息的关键点是多个类型映射合作以包装单个函数的方式。
在调用发生后, 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为我们这样做,使用在类型后添加括号的符号,而不是将其添加到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
}
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”typemap中,我将返回另一个包含新值的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变得简单:
%typemap(in,numinputs=0) int[2] (int temp[2]) {
// "In" typemap goes here:
memset(temp, 0, sizeof temp);
$1 = temp;
}
所以现在“in”typemap实际上根本不接受Python的任何输入。可以看作只是准备本机调用的输入。
通过旁白,您可以避免SWIG应用的名称损坏(使用{无法在同一函数上多次使用相同的类型映射的成本,或使用另一个具有名称冲突的类型映射) “in”typemap中的{1}}。我不建议这样做。
最后值得注意的是,我们可以将所有这些类型映射编写为更通用,并适用于任何固定大小的数组。为此,我们在typemap匹配中将2更改为“ANY”,然后在typemap主体内使用noblock=1
而不是2,因此最后的整个界面变为:
$1_dim0