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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (142)

2月12日编辑

我刚刚在一些C ++类中使用一些SWIG生成的Python包装器出现了一个奇怪的崩溃。似乎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之前,我们永远能读取对象被销毁theFoovalue领域。在gdb中追逐事物,这显然正在发生的事情。因此,当我们读取.valueBar().theFoo,C ++已经被破坏(并被其他一些堆分配覆盖).theFoo。在我的实际情况中,这导致了段错误。

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

提问于
用户回答回答于

第二次更新

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

所以显而易见的解决方案是防止python Bar过早地破坏。这是一个涉及子类化的略微hackish解决方案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

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

第一次更新

包装代码没有告诉我任何内容,所以我查看了swigexample.py。这也没有产生任何结果。当我尝试Bar在纯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

现在我们从pyfoobar导入Bar:

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

这种行为来自Python!

原答案

看起来这里肯定有一些垃圾收集战斗......这里有一些关于SWIG内存管理的相关信息。基于此,看起来%newobject指令可能正是您所寻找的; 但我尝试了几个变种,无法让它给python控制theFoo

>>> from swigexample import Bar
>>> b = Bar()
>>> b.theFoo.value
1
>>> b.theFoo.thisown
False
>>> Bar().theFoo.value
0
>>> Bar().theFoo.thisown
False

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

C现在持有对象的引用---你可能不希望Python破坏它。

但我不确定。我将查看swigexample_wrap代码,看看我是否可以找出~Bar被调用的时间。

用户回答回答于

解决方案是将%naturalvar添加到.i文件中,如下所示:

%naturalvar Bar::theFoo;
%include "Example.hpp"

这会导致SWIG返回Foo的副本而不是对它的引用,这解决了Python所做的积极临时对象清理的问题。

所属标签

可能回答问题的人

  • 人生的旅途

    10 粉丝484 提问5 回答
  • 天使的炫翼

    17 粉丝531 提问5 回答
  • 不吃貓的鱼oo

    4 粉丝466 提问4 回答
  • 找虫虫

    0 粉丝0 提问4 回答

扫码关注云+社区

领取腾讯云代金券