swig/python: 对象不支持索引

swig/python: object does not support indexing

给定这组文件:

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

setup.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"],
      )

test.py:

from foo import intFoo

a = intFoo(10,20,30)
print(dir(a))
a.info()
print(a[2])

我构建扩展 运行ning:

python setup.py build_ext --force -i

但是当我尝试 运行 test.py 我会得到:

TypeError: 'foo.intFoo' object does not support indexing

foo.i 中的语句 extend 是在任何其他 SO 相关线程上建议的答案,这意味着我在这里使用不正确。谁能解释一下如何解决这个问题,以便当我 运行 test.py 能够成功使用 [] 运算符时?

另一次尝试:

我已将错误的答案(指出 %extent 只能在没有 -builtin 选项的情况下使用)更改为有效的答案,但同样只能在没有 'built-in' 选项的情况下使用

setup.py

import os
import sys
from setuptools import setup, Extension

foo_module = Extension('_foo',
                           sources=[
                               'foo.i', 'foo.cpp'
                           ],
                           swig_opts=['-c++'],
                           include_dirs=['.']
                           )

setup(name='foo',
      version='0.1',
      platforms=['Windows', 'Linux'],
      ext_modules=[foo_module],
      py_modules=["foo"],
      )

foo.i

%module foo

%{
#include "foo.h"
%}

%include "carrays.i"
%array_functions(int, intArray);

%include "foo.h"

%extend Foo<int> {
%pythoncode %{
def __getitem__(self, id):
  return _foo.intArray_getitem(self.m,id)             
%}
};

%template(intFoo) Foo<int>;

请注意,扩展程序如何生成 Python 代码,让您可以做很多复杂的事情。

旧答案:

根据 SWIG 文档,python 层被剥离,因此 %extend 功能被忽略(这是不正确的,未创建代理对象)

参见 http://www.swig.org/Doc3.0/SWIGDocumentation.html

36.4.2 Built-in Types

When -builtin is used, the pure python layer is stripped off. Each wrapped class is turned into a new python built-in type which inherits from SwigPyObject, and SwigPyObject instances are returned directly from the wrapped methods. For more information about python built-in extensions, please refer to the python documentation:

(在整个示例中,我使用的是您的 foo.i 的第一个版本)

首先,您需要在 %template 指令之前指定 %extend 才能生效。

修复后,我们现在从您的 %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 添加的方法并不是您要将它们添加到的 class 的真正成员。要在此上下文中访问 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 classes 的工作方式不同。您已成功添加 __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__,因为该函数已在插槽中注册,应该可以在没有 %extend 的情况下调用 operator[]例如)

最后你可能想将 return 类型更改为 const T0&,或者将 Python 类型写入代理对象以更好地引用非 const int。