鉴于这组文件:
foo.h:
#pragma once
#include <stdio.h>
template <class T0> class Foo {
public:
T0 m[3];
Foo(const T0 &a, const T0 &b, const T0 &c) {
m[0] = a;
m[1] = b;
m[2] = c;
}
void info() { printf("%d %d %d\n", m[0], m[1], m[2]); }
// T0 &operator[](int id) { return ((T0 *)m)[id]; }
};
foo.cpp:
#include "foo.h"
foo.i(尝试 1):
%module foo
%{
#include "foo.h"
%}
%include "foo.h"
%template(intFoo) Foo<int>;
%extend Foo{
T0& __getitem__(int id) { return ((T0 *)m)[id]; }
}
设置.py:
import os
import sys
from setuptools import setup, Extension
foo_module = Extension('_foo',
sources=[
'foo.i',
'foo.cpp'
],
swig_opts=['-c++', '-py3', '-builtin'],
include_dirs=['.']
)
setup(name='foo',
version='0.1',
platforms=['Windows', 'Linux'],
ext_modules=[foo_module],
py_modules=["foo"],
)
测试.py:
from foo import intFoo
a = intFoo(10,20,30)
print(dir(a))
a.info()
print(a[2])
我构建了运行的扩展:
python setup.py build_ext --force -i
但是当我尝试运行 test.py 时,我会得到:
TypeError: 'foo.intFoo' object does not support indexing
extend
中的 foo.i
语句是在任何其他 SO 相关线程上建议的答案,这意味着我在这里错误地使用了它。谁能解释一下如何解决这个问题,以便当我运行 test.py
时能够成功使用 []
运算符?另一种尝试:
%module foo
%{
#include "foo.h"
%}
%include "foo.h"
%template(intFoo) Foo<int>;
%extend intFoo{
T0& __getitem__(int id) { return ((T0 *)m)[id]; }
}
抛出这个错误
TypeError: 'foo.intFoo' object does not support indexing
%module foo
%{
#include "foo.h"
%}
%include "foo.h"
%extend Foo{
T0& __getitem__(int id) { return ((T0 *)m)[id]; }
}
%template(intFoo) Foo<int>;
抛出这个错误
foo_wrap.cpp(3808): error C2065: 'm': undeclared identifier
最佳答案
(在整个示例中,我正在使用您的第一个版本的 foo.i)
首先,您需要在 %extend
指令之前指定 %template
才能生效。
一旦我们解决了这个问题,我们现在从你的 %extend
代码中得到一个编译器错误:
foo_wrap.cpp: In function 'int& Foo_Sl_int_Sg____getitem__(Foo<int>*, int)':
foo_wrap.cpp:3705:85: error: 'm' was not declared in this scope
发生这种情况是因为您使用
%extend
添加的方法并不是您将它们添加到的类的真正成员。要在此上下文中访问 m
,我们需要使用 $self->m
来引用它。 SWIG 将为我们使用适当的变量 replace $self
。 (值得快速浏览一下生成的代码以了解其工作原理)调试类型映射或扩展时的一个有用提示是搜索您在 SWIG 生成的输出中编写的代码 - 如果它不存在,那么它不会按照您的想法应用。
因此,一旦我们修复了未声明
m
的错误,我们就会遇到另一个问题,因为您已使用 -builtin
进行编译:In [1]: import foo
In [2]: f=foo.intFoo(1,2,3)
In [3]: f[0]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-e71eec16918d> in <module>()
----> 1 f[0]
TypeError: 'intFoo' object does not support indexing
In [4]: f.__getitem__(0)
Out[4]: <Swig Object of type 'int *' at 0xa8807d40>
即使您添加了
__getitem__
,使用 f[n]
进行索引仍然不起作用。发生这种情况是因为 Python 运算符重载中的纯 C-API 类的工作方式不同。您已经成功添加了__getitem__
方法,但是Python正在builtin type's slots(特别是mp_subscript)中寻找执行操作的方法。所以我们也需要解决这个问题。完成后,工作 foo.i 看起来像:%module foo
%{
#include "foo.h"
%}
%feature("python:slot", "mp_subscript", functype="binaryfunc") Foo::__getitem__;
%include "foo.h"
%extend Foo{
T0& __getitem__(int id) { return ((T0 *)$self->m)[id]; }
}
%template(intFoo) Foo<int>;
所以现在我们可以做你想做的:
In [1]: import foo
In [2]: f=foo.intFoo(1,2,3)
In [3]: f[0]
Out[3]: <Swig Object of type 'int *' at 0xb4024100>
(实际上您不必再将其称为
__getitem__
,因为该函数已在插槽中注册,因此应该可以在没有 operator[]
的情况下调用 %extend
,例如)最后,您可能希望将返回类型更改为
const T0&
,或者编写一个 Python 类型来更好地代理非常量 int 引用的对象。