如何处理Python

时间:2018-01-06 23:26:16

标签: python c++ swig

我使用SWIG将C ++库包装为Python库。 C ++库公开了抽象类,供用户继承它,因此我们使用SWIG中的控制器来处理。它的工作原理很好(有一些调整)。

一个问题是这个C ++类有两个方法如下:

class Base {
  void* getObject();
  void  doSomething(void* o);
}

希望用户实现这些方法,然后将用户在getObject()中返回的对象传递给doSomething()方法。

问题在于,当浏览SWIG时,Python中的doSomething()方法会收到一个包装类型为“void *”的SwigPyObject,因此我们不能像我们希望的那样使用原始的Python对象方法。 而且不是选择,因为它是Python(或者是它?)。

有没有人有任何见解?

我在这里和那里发现了一些相关的问题,但它们似乎都没有解决我的问题,而且我已经尝试了许多方法来解决它,但没有任何成功。

如果您需要更多详细信息,请告诉我,我会提供。

非常感谢!

1 个答案:

答案 0 :(得分:0)

首先,我们希望将您的代码变为真实可运行的东西。我根据你所展示的小代码编写了我自己的test.hh,让我们在某种程度上运用这个设计:

class Base {
public:
  void runMe() {
    std::cerr << "Getting object\n";
    void *result = getObject();
    std::cerr << "Got: " << result << "\n";
    doSomething(result);
    std::cerr << "Did a thing\n";
  }

  virtual ~Base() {}
protected:
  virtual void* getObject() = 0;
  virtual void  doSomething(void* o) = 0;
};

我们最初可以将其包装为:

%module(directors="1") test

%{
#include "test.hh"
%}

%feature("director") Base;

%include "test.hh"

并制作一个测试用例,以展示我们如何在Python中使用它:

import test

class Foobar(test.Base):
    def getObject(self):
        return [1,2,3]

    def doSomething(self, thing):
        print(thing)

f=Foobar()
f.runMe()

但是这个阶段还没有成功,因为我们没有告诉SWIG如何在Python中有意义地处理void*

这里的一般想法是我们想让void*在接口内用作PyObject*。我们可以通过directorin和directorout typemap配对来做到这一点。从广义上讲,我们需要解决两个问题:

  1. 如何使引用计数正常工作而不泄漏?
  2. 如果void*我们得到的PyObject*真的不是getObject()会怎样?
  3. 如果我们假设doSomething()调用和PyObject调用之间存在1:1映射,那么引用计数相当简单,我们可以在界面中编写两个类型映射保留对void*的引用,然后在需要时将其从%module(directors="1") test %{ #include "test.hh" %} %feature("director") Base; %typemap(directorout) void *getObject %{ Py_INCREF($1); $result = $1; %} %typemap(directorin) void *o %{ $input = static_cast<PyObject*>($1); // Director call will decref when we're done here - it assumes ownership semantics, not borrowed %} %include "test.hh" 转回(请注意,我们还通过添加1:1限制完全回避了问题#2)。

    因此,使用这两个类型图,我们的界面变为:

    swig -Wall -python -py3 -c++ test.i
    g++ -Wall -Wextra  -shared -o _test.so -I/usr/include/python3.5 test_wrap.cxx -std=c++11 -fPIC
    python3 run.py 
    Getting object
    Got: 0x7fce97b91c48
    [1, 2, 3]
    Did a thing
    

    当我们这样测试时:

    runMe

    但是,如果我们将此处的sematics更改为不完全是1:1,那么我们就会遇到问题,例如make void runMe() { std::cerr << "Getting object\n"; void *result = getObject(); std::cerr << "Got: " << result << "\n"; doSomething(result); std::cerr << "Second time\n"; doSomething(result); std::cerr << "Did a thing\n"; } 就是这样:

    doSomething

    现在哪个段错误,因为在第一次调用Py_INCREF之后引用已递减。

    在这个阶段,显而易见的事情是在directorin类型地图中添加对getObject()的调用,但这不完全是故事 - 我们永远不会 call现在发布runMe()的结果,它只是在Base结尾处超出范围。

    我倾向于解决的方法是在virtual void cleanupThing(void* o) {} // Default nothing, not mandatory 界面添加另一个电话:

    %rename

    有了这个,我们可以让你的SWIG接口实现(并隐藏,如果我们想要)完全在Python控制器内部调用。这样做的方法是使用一些%ignorerunMe以及一些宏观技巧:

    因此,通过以下对SWIG界面的调整,我们现在可以正确地使用%module(directors="1") test %{ #include "test.hh" %} %feature("director") PyBase; %typemap(directorout) void *getObject %{ Py_INCREF($1); $result = $1; %} %typemap(directorin) void *o %{ $input = static_cast<PyObject*>($1); Py_INCREF($input); // Not borrowed now // Director call will decref when we're done here %} // Python won't even know cleanupThing existed because we use it internally in the Python binding %ignore PyBase::cleanupThing; %feature("nodirector") PyBase::cleanupThing; // This is a sleight of hand trick with SWIG so we can add another type into the hierarchy without anyone really noticing %rename(Base) PyBase; %{ class PyBase : public Base { void cleanupThing(void *o) { Py_DECREF(o); } }; %} #define Base PyBase %include "test.hh" 的第二个版本:

    cleanupThing

    runMe打电话给void runMe() { std::cerr << "Getting object\n"; void *result = getObject(); std::cerr << "Got: " << result << "\n"; doSomething(result); std::cerr << "Second time\n"; doSomething(result); std::cerr << "Did a thing\n"; cleanupThing(result); }

    Getting object
    Got: 0x7ff65dccfd08
    [1, 2, 3]
    Second time
    [1, 2, 3]
    Did a thing
    

    现在运行时确实给出:

    checked

    (特别是如果语义比简单地来回传递到同一实例的局部变量更复杂,则存在其他可能的解决方案。)