swig / python:object不支持索引

时间:2017-06-01 13:27:10

标签: python c++ swig

鉴于这组文件:

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(Attempt1):

%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])

我构建了扩展程序:

python setup.py build_ext --force -i

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

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

extend中的语句foo.i是在任何其他SO相关主题上建议的答案,这意味着我在这里错误地使用它。任何人都可以解释如何解决这个问题,这样当我运行test.py时能够成功使用[]运算符吗?

另一种尝试:

  • ATTEMPT2:

    %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

  • Attempt3

    %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

2 个答案:

答案 0 :(得分:7)

(在这个例子中,我正在使用你的第一个版本的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类型编写为代理对象,以便更好地进行非const int引用。

答案 1 :(得分:3)

我更改了错误的答案(说%extent只能在没有-builtin选项的情况下使用)才能使用有效的内容,但只有在没有内置&#39;选项

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内置类型

     

当使用-builtin时,将剥离纯python层。每   wrapped类被转换为一个继承的新的python内置类型   从SwigPyObject,直接返回SwigPyObject实例   从包装的方法。有关python内置的更多信息   扩展,请参阅python文档: