SWIG Python包装器中临时对象的生命周期(?)

时间:2011-02-12 00:45:27

标签: c++ python swig lifetime temporary-objects

2月12日编辑

我最近刚刚使用一些SWIG生成的Python包装器为一些C ++类提出了一个奇怪的崩溃。似乎SWIG和Python的结合有点急于清理临时值。事实上,他们非常渴望在他们还在使用的时候进行清理。一个非常简洁的版本看起来像这样:

/* Example.hpp */
struct Foo {
    int value;
    ~Foo();
};

struct Bar {
    Foo theFoo;
    Bar();
};

/* Example.cpp */
#include "Example.hpp"
Bar::Bar()  {theFoo.value=1;}
Foo::~Foo() {value=0;}

/* Example.i */
%module Example
%{
#include "Example.hpp"
%}
%include "Example.hpp"

我在.i文件上运行SWIG(1.3.37),然后在Python中运行:

Python 2.4.3 (#1, Sept 17 2008, 16:07:08)
[GCC 4.1.2 20071124 (Red Hat 4.1.2-41)] on linux2
Type "help", "copyright", "credits", or "license" for more information.
>>> from Example import Bar
>>> b=Bar()
>>> print b.theFoo.value      # expect '1', since Bar's constructor sets this
1
>>> print Bar().theFoo.value  # expect '1', since we're still using the Foo object
26403424

似乎在第二个实例中,临时Bar对象在我们阅读theFoo的{​​{1}}字段之前被销毁。在gdb中追逐事物,这显然正在发生的事情。因此,当我们从value读取.value时,C ++已经销毁(并用其他一些堆分配覆盖)Bar().theFoo。在我的实际情况中,这会导致段错误。

是否有任何SWIG指令或技巧可以添加到我的.theFoo文件中,以使Example.i在此处返回Bar().theFoo.value

2 个答案:

答案 0 :(得分:2)

第二次更新

我们知道基本问题是python会立即销毁Bar。当在python中实现Bar时,python的gc知道仍然存在对theFoo的引用,因此不会销毁它。但是当在c ++中实现Bar时,python会调用c ++析构函数,它会自动销毁theFoo以及Bar.

所以显而易见的解决方案是防止python过早地破坏Bar。这是一个稍微讨厌的解决方案,涉及子类化Bar

class PersistentBar(swigexample.Bar):
    lastpbar = None
    def __init__(self):
        super(PersistentBar, self).__init__()
        PersistentBar.lastpbar = self

这会保存对最后创建的Bar的引用,以便不会立即销毁它。创建新Bar时,将删除旧的__del__。 (我的旧版本很傻;不需要为此重写cout << "deleting Foo "。)这是输出(在Foo的析构函数中使用>>> from test import persistentBar >>> persistentBar().theFoo.value 1 >>> persistentBar().theFoo.value deleting Foo 1 >>> persistentBar().theFoo.value deleting Foo 1 ):

theFoo

我仍然不喜欢这个。在装饰器中隔离“持久”行为可能更好;我也尝试了它并且它有效(如果你想看到代码让我知道)。以某种方式告诉python处理破坏Bar本身肯定会更好,但我无法弄清楚如何做到这一点。

第一次更新

包装代码没有告诉我任何内容,所以我查看了swigexample.py。这也没有产生任何结果。当我尝试在纯python中复制# pyfoobar.py class Foo(object): def __init__(self): self.value = -1 class Bar(object): def __init__(self): self.theFoo = Foo() self.theFoo.value = 1 def __del__(self): self.theFoo.value = 0 时,事情变得更加清晰了:

>>> from pyfoobar import Bar
>>> b = Bar()
>>> b.theFoo.value
1
>>> Bar().theFoo.value
0

现在我们从pyfoobar导入Bar:

theFoo

此行为来自Python!

原始答案

这里似乎肯定有一些垃圾收集战斗......这里有一些关于SWIG Memory Management的相关信息。基于此,看起来%newobject指令可能正是您所寻找的;但是我尝试了几种变体而无法让它对>>> from swigexample import Bar >>> b = Bar() >>> b.theFoo.value 1 >>> b.theFoo.thisown False >>> Bar().theFoo.value 0 >>> Bar().theFoo.thisown False 进行python控制:

~Bar

我开始怀疑这是故意的;看来上面链接中的这一行与此相关:

  

C现在持有对该的引用   对象---你可能不想要   用Python来摧毁它。

但我不确定。我将查看swigexample_wrap代码,看看我是否能够确定何时调用{{1}}。

答案 1 :(得分:0)

解决方案是像这样将%naturalvar添加到您的.i文件:

Question on subsection

* Slide title

Here is a minimal go present slide.  Why is the slide subsection text
at the bottom of the slide?

** slide subsection

Hello, this text is part of the subsection.

这导致SWIG返回Foo的副本而不是对其的引用,从而解决了Python进行的主动临时对象清理的问题。

相关问题