2011-11-22 2 views
6

オブジェクトへの参照をweak_ptrとして保存したいと思います。純粋なC++、次の作品では:boost :: pythonとweak_ptr:stuffが消える

#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 

、私はブーストのpythonであることをカプセル化する場合:今

#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() 
私は同じ方法(のsetValue)まだだながら

...そして結果は...

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

はそう明らかに、ストア::値から のshared_ptrを取得する問題はありません。しかし、私がこの文脈から抜け出すとすぐに、何も残っていません!

これはどのようにすることができますか? PythonはsetValueの引数としてブランドの新しい(無用な)shared_ptrを渡していますが、呼び出しの終わりに破棄されますか?私はここで失われています。

+1

Hmm ...メッセージを印刷し、破壊されるかどうかを調べるためのデストラクタを 'Empty'に追加します。また、 'shared_ptr'を保存するとどうなりますか? –

+1

私の推測では、ある場所では 'shared_ptr'は' :: boost :: shared_ptr'を参照し、別の場所では ':: std :: shared_ptr'を参照します。異なる名前空間の名前を複数の 'using namespace'宣言に引き込むために、' typedef'を使うことを大いに好みます。 – Omnifarious

+1

これはこのようなものだと思いますか? http://mail.python.org/pipermail/cplusplus-sig/2009-November/014981.html – HostileFork

答えて

4

これは非常に興味があります。私はstd対支配力の可能性を排除し、いくつかの健全性チェックで周り遊んだと私はそれが何かを教えてくださいブーストのpythonは共有ポインタそれを壊しているしています。

オブジェクトのコンストラクタ/デストラクタをトレースすると、EmptyとStoreのライフタイムは期待通りに管理されます(コピーは発生しません)。

非常に興味深い事がshared_from_thisweak_ptr<>.lock()がない場合でも、動作し続け、そして、実際には、(shared_from_thisから)新しい共有ポインタから作成された新しい弱いポインタが作業を行うということです。

これは私にthe thread linked in the commentsにつながっています。弱いポインタを破っているDeleterと参照カウントで何かが向上しているようです。我々は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>} 
    } 
    } 
} 

ここで共有カウントのアドレスが異なっています:これは別の共有ポインタインスタンスです...私たちは何とか同じアドレスへの2つの異なる共有ポインタを作成しました。これはすぐにダブルフリーになると期待しているので、これは非常に悪いことです。

ただし、そうではありません。 (私は誰もがアイデアを持っている場合、これをさらに理解したいと思っていますか?)

正直言って(おそらくここでは微妙なことが起こっていると思われますが)、いずれにしてもこれは解決策を指しています。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

ありがとうございます!関連する主題では、これは私が短時間で遭遇する2番目のスマートポインタ関連のバグです([最初の1つはここにあります](http://stackoverflow.com/questions/8203200/boostpython-and-seterase-weird-behaviour)) 。 std :: tr1の実装がより良いと期待できますか? – girodt

+0

好奇心、これの根本的な原因が関連しているのだろうか。私は参照カウントを比較する根拠を理解することができると思います。これは共有ポインタのセットのアイデンティティであり、いくつかの奇妙な状況下では、共有オブジェクトへの2つの異なる共有ポインタのセットがあります。 2つのセットのうち少なくとも1つの削除プログラムは実際に削除していませんでした。しかし、まだやや直感的ではない... – James

3

別の回避策はshared_ptr、ないshared_ptrオブジェクト自体への参照を渡すことです。その場合、明らかにboost::python-Wrapperはこの奇妙な別物を作成していませんshared_ptr

Btw。私はshared_ptr.pn.pi_で指されるクラスの型を見ます。shared_ptrがPythonに渡されたのは、sp_counted_impl_p<POINTEE_TYPE>オブジェクトへのポインタが含まれているのに対し、shared_ptrはPythonから戻って渡されたポインタですa sp_counted_impl_pd<void*,shared_ptr_deleter> objectsmart_ptr/detail/sp_counted_impl.hpp参照) pd variantには、pointeeへの参照が含まれていません。私はこのsp_counted_impl_pdオブジェクトが何とかsp_counted_impl_pオブジェクトを参照していると思われます。これは、shared_ptrの参照カウントがPythonから戻って0に落ちたときにデストラクタが呼び出されない理由を説明します。 weak_ptrがこの場合に動作しないという事実は単にバグかもしれません。

関連する問題