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
的 value
字段之前。在 gdb 中追踪事情,这显然是正在发生的事情。因此,当我们从 Bar().theFoo
读取 .value
时,C++ 已经销毁了(并被其他一些堆分配覆盖).theFoo
。代码>.在我的实际情况中,这导致了段错误。
是否有任何 SWIG 指令或技巧可以添加到我的 Example.i
文件中,以使 Bar().theFoo.value
在此处返回 1
?
Edited 12 Feb
I've just recently come up with an odd crash using some SWIG-generated Python wrappers for some C++ classes. It seems that the combination of SWIG and Python together are somewhat eager to clean up temporary values. So eager, in fact, that they're cleaned up while they're still being used. A significantly condensed version looks like this:
/* 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 run SWIG (1.3.37) on the .i file, and then in Python, have:
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
It seems that in the second instance, the temporary Bar
object is destroyed before we ever get to read theFoo
's value
field. Chasing things around in gdb, this is clearly what's happening. And so by the time we read .value
from Bar().theFoo
, C++ has already destroyed (and overwritten with some other heap allocation) .theFoo
. In my actual situation, this is causing a segfault.
Is there any SWIG directive or trick that I can add to my Example.i
file to make Bar().theFoo.value
return 1
here?
发布评论
评论(2)
第二次更新:
我们知道基本问题是 python 立即销毁
Bar
。当Bar
在 python 中实现时,python 的 gc 知道仍然存在对theFoo
的引用,因此不会销毁它。但是当Bar
在 C++ 中实现时,Python 会调用 C++ 析构函数,它会自动销毁theFoo
以及Bar。
所以显而易见的解决方案是防止python 过早地销毁
Bar
。这是一个稍微有点黑客的解决方案,涉及子类化Bar
:这会保存对最后创建的
Bar
的引用,以便它不会立即被销毁。当创建新的Bar
时,旧的将被删除。 (我的旧版本很愚蠢;不需要为此重写 __del__ 。)这是输出(在Foo< 中使用
cout << "deleting Foo "
/code> 的析构函数):我仍然不喜欢这个。将“持久”行为隔离在装饰器中可能会更好;我也尝试过并且有效(如果您想查看代码,请告诉我)。以某种方式告诉 python 处理销毁
theFoo
本身肯定会更好,但我不知道如何做到这一点。第一次更新:
包装代码没有告诉我任何信息,所以我查看了 swigexample.py。那也一无所获。当我尝试在纯 python 中复制
Bar
时,事情变得更加清晰:现在我们从 pyfoobar 导入 Bar:
这种行为来自 Python!
原始答案:
看来这里肯定存在一些垃圾收集战斗......这里有一些关于 SWIG 内存管理。基于此,看起来 %newobject 指令可能就是您正在寻找的;但我尝试了几种变体,但无法让它让 python 控制
theFoo
:我开始怀疑这是故意的;似乎上面链接中的这一行与这里相关:
但我不确定。我将查看 swigexample_wrap 代码,看看是否可以确定
~Bar
何时被调用。Second Update:
Well we know that the basic problem is that python destroys
Bar
immediately. WhenBar
is implemented in python, python's gc knows that there's still a reference totheFoo
, and so does not destroy it. But whenBar
is implemented in c++, python calls the c++ destructor, which automatically destroystheFoo
along withBar.
So the obvious solution is to prevent python from destroying
Bar
prematurely. Here's a slightly hackish solution involving subclassingBar
:This saves a reference to the last
Bar
created so that it isn't destroyed right away. When a newBar
is created, the old one is deleted. (My old version was silly; no need to override__del__
for this.) Here's the output (withcout << "deleting Foo "
inFoo
's destructor):I still don't love this. It might be better to sequester the "persistent" behavior in a decorator; I tried that too and it worked (if you want to see the code let me know). It would definitely be better to somehow tell python to handle destroying
theFoo
itself, but I can't figure out how to do that.First Update:
The wrap code told me nothing, so I looked in swigexample.py. That also yielded nothing. Things became clearer when I tried duplicating
Bar
in pure python:Now we import Bar from pyfoobar:
This behavior is coming from Python!
Original Answer:
It seems like there's definitely some garbage collection combat at work here... Here's some related information on SWIG Memory Management. Based on this, it looks like the %newobject directive might be what you're looking for; but I tried several variations and couldn't get it to give python control over
theFoo
:I'm beginning to suspect that this is intentional; seems like this line from the above link is relevant here:
But I'm not certain. I'm going to look at the swigexample_wrap code to see if I can figure out when
~Bar
is being called.解决方案是将 %naturalvar 添加到您的 .i 文件中,如下所示:
这会导致 SWIG 返回 Foo 的副本而不是对它的引用,这解决了 Python 所做的积极临时对象清理的问题。
The solution is to add %naturalvar to your .i file like this:
This causes SWIG to return a copy of Foo instead of a reference to it, which solves the problem of aggressive temporary object cleanup that Python does.