swig/python:object不支持索引

BPL*_*BPL 7 c++ python 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]; }
};
Run Code Online (Sandbox Code Playgroud)

Foo.cpp中:

#include "foo.h"
Run Code Online (Sandbox Code Playgroud)

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]; }
}
Run Code Online (Sandbox Code Playgroud)

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"],
      )
Run Code Online (Sandbox Code Playgroud)

test.py:

from foo import intFoo

a = intFoo(10,20,30)
print(dir(a))
a.info()
print(a[2])
Run Code Online (Sandbox Code Playgroud)

我构建了扩展运行:

python setup.py build_ext --force -i
Run Code Online (Sandbox Code Playgroud)

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

TypeError: 'foo.intFoo' object does not support indexing
Run Code Online (Sandbox Code Playgroud)

声明extendfoo.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]; }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    引发此错误 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>;
    
    Run Code Online (Sandbox Code Playgroud)

    引发此错误 foo_wrap.cpp(3808): error C2065: 'm': undeclared identifier

Fle*_*exo 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
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为您添加的方法%extend实际上并不是您要添加它们的类的成员.要m在此上下文中访问,我们需要改为引用它$self->m.SWIG将为我们替换$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>
Run Code Online (Sandbox Code Playgroud)

即使你已添加__getitem__,索引f[n]仍然无法正常工作.这种情况正在发生,因为Python中的纯C-API类操作符重载不能以相同的方式工作.您已__getitem__成功添加了一个方法,但Python正在查看内置类型的插槽(特别是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>;
Run Code Online (Sandbox Code Playgroud)

所以现在我们可以做你想做的事:

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>
Run Code Online (Sandbox Code Playgroud)

(你实际上并没有叫它__getitem__任何更多,因为函数获取插槽登记的,应该可以调用operator[]没有%extend在所有的情况下)

最后,您可能希望将返回类型更改为const T0&,或者将Python类型写入代理对象以更好地进行非const int引用.