2011-11-22 32 views
6

我想存儲一個對象作爲weak_ptr的參考。在純C++,下面的工作:的boost ::蟒蛇的weak_ptr:東西消失

#include <iostream> 

#include <boost/shared_ptr.hpp> 
#include <boost/weak_ptr.hpp> 

using namespace std; 
using namespace boost; 

struct Empty 
{ 
    Empty(){} 
}; 

struct Store 
{ 
    weak_ptr<Empty> value; 
    Store(){}; 

    void setValue(shared_ptr<Empty> v) { 
     cout << "storing " << v << endl; 
     this->value = weak_ptr<Empty>(v); 
     shared_ptr<Empty> v_ok = this->value.lock(); 
     if (v_ok) { 
      cout << "ok, v has been stored" << endl; 
     } 
    } 

    shared_ptr<Empty> getValue() { 
     shared_ptr<Empty> p = this->value.lock(); 
     if (p) { 
      cout << "stored value : " << p << endl; 
     } else { 
      cout << "there's nothing here !" << endl; 
     } 
     return p; 
    } 
}; 

int main() 
{ 
    shared_ptr<Empty> e(new Empty); 
    shared_ptr<Store> st(new Store); 

    st->setValue(e); 
    st->getValue(); 
    return 0; 
} 

編譯和運行這會給你:

%> ./a.out 
storing 0x8c6c008 
ok, v has been stored 
stored value : 0x8c6c008 

現在,如果我封裝與升壓蟒蛇:

#include <iostream> 

#include <boost/shared_ptr.hpp> 
#include <boost/python.hpp> 
#include <boost/weak_ptr.hpp> 

using namespace std; 
using namespace boost; 
using namespace boost::python; 

struct Empty 
{ 
    Empty(){} 
}; 

struct Store 
{ 
    weak_ptr<Empty> value; 
    Store(){}; 

    void setValue(shared_ptr<Empty> v) { 
     cout << "storing " << v << endl; 
     this->value = weak_ptr<Empty>(v); 
     shared_ptr<Empty> v_ok = this->value.lock(); 
     if (v_ok) { 
      cout << "ok, v has been stored" << endl; 
     } 
    } 

    shared_ptr<Empty> getValue() { 
     shared_ptr<Empty> p = this->value.lock(); 
     if (p) { 
      cout << "stored value : " << p << endl; 
     } else { 
      cout << "there's nothing here !" << endl; 
     } 
     return p; 
    } 
}; 

BOOST_PYTHON_MODULE (test) 
{ 
    class_< Empty, shared_ptr<Empty> >("Empty"); 

    class_< Store, shared_ptr<Store> >("Store") 
    .def("get",&Store::getValue) 
    .def("set",&Store::setValue); 
} 

現在小python腳本來嘗試一下

from test import * 

e = Empty() 
st = Store() 

st.set(e) 
st.get() 

......而結果是...

storing 0x9eb2a18 
ok, v has been stored 
there's nothing here ! 

如此明顯,而我仍然在同樣的方法(setValue方法),是沒有問題的檢索從商店::值 shared_ptr的。但是,一旦我擺脫這種情況,就什麼都沒有了!

這怎麼可能? python會傳遞一個全新的(無用的)shared_ptr作爲setValue的參數,然後在調用結束時被銷燬?我迷失在這裏。

+1

嗯...添加一個析構函數到'Empty'打印一條消息,看它是否被破壞。另外,如果你存儲'shared_ptr',會發生什麼? –

+1

我的猜測是,在一個位置'shared_ptr'指':: boost :: shared_ptr',而在另一個位置則指':: std :: shared_ptr'。我非常希望使用'typedef'將名稱從不同的名稱空間中提取到具有多個'using namespace'聲明。 – Omnifarious

+1

認爲它與此有關? http://mail.python.org/pipermail/cplusplus-sig/2009-November/014981.html – HostileFork

回答

4

這是非常奇怪的。我已經排除了性病與升壓共享指針的可能性,並有一些健全檢查玩耍了,而據我可以告訴它是提高Python是做在突破它的共享指針東西

跟蹤對象構造函數/析構函數,Empty和Store的生命週期按照您的預期進行管理(不會發生副本)。

非常有趣的事情是,shared_from_this繼續工作,即使weak_ptr<>.lock()不和,其實,從一個新的共享指針創建了一個新的弱指針(從shared_from_this)做的工作。

因此,這導致我the thread linked in the comments,似乎有什麼提升python正在做的刪除和引用計數,打破弱指針。

檢查共享指針在調試器這就是我們得到:

當我們調用setValue,這是爭論的樣子:

1: p = (const 'boost::shared_ptr<Empty>' &) @0x7fff5fbfe720: { 
    px = 0x100346900, 
    pn = { 
    pi_ = 0x100338dd0 
    } 
} 
> p *p.pn.pi_ 
$5 = (boost::detail::sp_counted_impl_pd<void*,boost::python::converter::shared_ptr_deleter>) { 
    <boost::detail::sp_counted_base> = { 
    _vptr$sp_counted_base = 0x10061aa30, 
    use_count_ = 2, 
    weak_count_ = 2 
    }, 
    members of boost::detail::sp_counted_impl_pd<void*,boost::python::converter::shared_ptr_deleter>: 
    ptr = 0x0, 
    del = { 
    owner = { 
     m_p = 0x10049db90 
    } 
    } 
} 

如果我們創建一個使用一個共享指針shared_from_this的論點,它看起來像這樣:

1: p = (const 'boost::shared_ptr<Empty>' &) @0x7fff5fbfe5e0: { 
    px = 0x100346900, 
    pn = { 
    pi_ = 0x1003468e0 
    } 
} 
> p *p.pn.pi_ 
$4 = (boost::detail::sp_counted_impl_pd<Empty*,boost::detail::sp_ms_deleter<Empty> >) { 
    <boost::detail::sp_counted_base> = { 
    _vptr$sp_counted_base = 0x10061b170, 
    use_count_ = 2, 
    weak_count_ = 2 
    }, 
    members of boost::detail::sp_counted_impl_pd<Empty*,boost::detail::sp_ms_deleter<Empty> >: 
    ptr = 0x0, 
    del = { 
    initialized_ = true, 
    storage_ = { 
     data_ = "\000i4\000\001\000\000\000?h4\000\001\000\000", 
     align_ = {<No data fields>} 
    } 
    } 
} 

有一件事是沒有在這裏:共享計數的地址是不同的:這是一個不同的共享指針實例...所以我們以某種方式創建了兩個不同的共享指針到同一個地址。這是一件非常糟糕的事情,因爲我們預計這很快會導致雙重自由。

但是,它沒有。 (我很渴望進一步瞭解這一點,如果有人有任何想法?)

我不知道爲什麼它沒有,說實話(這裏大概有些微妙的變化),但無論如何,這一切都表明了一個解決方案:我們可以使用shared_from_this來創建一個共享指針,從傳遞的值中,我們用可以用創建一個實際起作用的弱指針。

所以,包裹起來,這裏的修復:

#include <iostream> 
#include <boost/shared_ptr.hpp> 
#include <boost/python.hpp> 
#include <boost/weak_ptr.hpp> 
#include <boost/enable_shared_from_this.hpp> 

namespace bp = boost::python; 

struct Empty: boost::enable_shared_from_this<Empty>{ }; 

struct Store 
{ 
    boost::weak_ptr<Empty> value; 

    void setValue(boost::shared_ptr<Empty> const& v) { 
     value = boost::weak_ptr<Empty>(v->shared_from_this()); 
     boost::shared_ptr<Empty> v_ok = value.lock(); 
     if (v_ok) { 
      std::cout << "ok, v has been stored" << std::endl; 
     } 
    } 

    boost::shared_ptr<Empty> getValue() { 
     boost::shared_ptr<Empty> p = value.lock(); 
     if (p) { 
      std::cout << "stored value : " << p << std::endl; 
     } else { 
      std::cout << "there's nothing here !" << std::endl; 
     } 
     return p; 
    } 
}; 

BOOST_PYTHON_MODULE (libmylibinterface) 
{ 
    bp::class_< Empty, boost::shared_ptr<Empty> >("Empty",bp::init<>()) 
     ; 

    bp::class_< Store, boost::shared_ptr<Store> >("Store") 
     .def("get",&Store::getValue) 
     .def("set",&Store::setValue); 

} 
+0

謝謝!在一個相關的主題上,這是我在短時間內偶然發現的第二個智能指針相關錯誤([first one here])(http://stackoverflow.com/questions/8203200/boostpython-and-seterase-weird-behaviour)) 。我能期待std :: tr1實現更好嗎? – girodt

+0

好奇,我不知道這是否與此有關。我想我可以理解比較引用計數的原理 - 因爲這是一組共享指針的身份,並且在一些奇怪的情況下,可能有兩組不同的共享指針指向共享對象,如果兩套中至少有一套的刪除器實際上並沒有刪除。儘管如此,仍然有點不直觀 – James

3

另一個解決方法是在參考傳遞給shared_ptr,而不是shared_ptr對象本身。在這種情況下,明顯boost::python-Wrapper不會產生這種奇怪的獨立shared_ptr

Btw。我看看shared_ptr.pn.pi_指向的類的類型,您會發現發給Python的「原始」shared_ptr包含指向sp_counted_impl_p<POINTEE_TYPE>對象的指針,而從Python傳回的shared_ptr包含一個指向a sp_counted_impl_pd<void*shared_ptr_deleter> object(請參見smart_ptr/detail/sp_counted_impl.hpp) pd變體不包含對指針對象的引用。我懷疑這個sp_counted_impl_pd對象是以某種方式引用sp_counted_impl_p對象。這可以解釋爲什麼當從Python返回的shared_ptr的引用計數下降到零時,不會調用析構函數。在這種情況下weak_ptr不起作用的事實可能只是一個錯誤,然後..?