簡體   English   中英

SWIG的Python包裝器中的臨時對象的生存期(?)

[英]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對象在我們可以讀取theFoovalue字段之前被銷毀了。 在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM