[英]Lifetime of temporary objects in SWIG's Python wrappers (?)
編輯於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
對象在我們可以讀取theFoo
的value
字段之前被銷毀了。 在gdb中追逐事物,這顯然是正在發生的事情。 因此,當我們從Bar().theFoo
讀取.value
,C ++已被破壞(並被其他堆分配覆蓋) .theFoo
。 在我的實際情況下,這導致了段錯誤。
是否可以將任何SWIG指令或技巧添加到Example.i
文件中,以使Bar().theFoo.value
在此處返回1
?
第二次更新 :
好吧,我們知道基本的問題是python立即破壞了Bar
。 當Bar
在python中實現時,python的gc知道仍然有對theFoo
的引用,因此不會破壞它。 但是,當Bar
用c ++實現時,python會調用c ++析構函數,該析構函數會與Bar.
一起自動銷毀theFoo
Bar.
因此,顯而易見的解決方案是防止python過早破壞Bar
。 這是一個涉及Bar
子類的解決方案:
class PersistentBar(swigexample.Bar):
lastpbar = None
def __init__(self):
super(PersistentBar, self).__init__()
PersistentBar.lastpbar = self
這節省了最后一個參考Bar
創建,使其不被破壞的時候了。 創建新的Bar
,舊Bar
將被刪除。 (我的舊版本很傻;不需要為此重寫__del__
。)這是輸出(在Foo
的析構函數中使用cout << "deleting 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。 那也什么也沒有產生。 當我嘗試在純python中復制Bar
時,事情變得更加清晰:
# 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 Memory Management的一些相關信息。 基於此,您似乎正在尋找%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進行的主動臨時對象清理的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.