boost::python 和weak_ptr:东西消失了
我想将对对象的引用存储为weak_ptr。在纯 C++ 中,以下工作:
#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
现在,如果我用 boost 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()
......结果是......
storing 0x9eb2a18
ok, v has been stored
there's nothing here !
所以显然,同时我仍然采用相同的方法(setValue),检索a没有问题 来自 Store::value 的共享指针。但一旦脱离了这个环境,就什么都没有了!
怎么会这样? python是否传递一个全新的(无用的)shared_ptr作为setValue的参数,然后在调用结束时销毁它?我在这里迷路了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是非常令人好奇的。我已经排除了 std 与 boost 共享指针的可能性,并进行了一些健全性检查,据我所知,boost python 对共享指针所做的事情会破坏它。
跟踪对象构造函数/析构函数,Empty 和 Store 的生命周期按照您的预期进行管理(不会发生复制)。
非常有趣的事情是,
shared_from_this
继续工作,即使weak_ptr<>.lock()
不工作,而且事实上,从新共享指针(来自shared_from_this)创建的新弱指针确实有效。因此,这引导我评论中链接的线程 ,似乎 boost python 正在使用删除器和引用计数来破坏弱指针。
检查调试器中的共享指针,这就是我们得到的结果:
当我们调用
setValue
时,参数如下所示:如果我们使用
创建共享指针参数上的shared_from_this
,它看起来像这样:这里需要注意一件事:共享计数的地址不同:这是一个不同的共享指针实例......所以我们'我们以某种方式创建了两个不同的共享指针 地址。这是一件极其糟糕的事情,因为我们预计这很快就会导致双重释放。
然而,事实并非如此。 (如果有人有任何想法,我很想进一步了解这一点?)
说实话,我不知道为什么不这样做(大概这里发生了一些微妙的事情),但无论如何,所有这些确实指出了一个解决方案:我们可以使用
shared_from_this
根据传递的值创建一个共享指针,我们可以使用它来创建一个实际有效的弱指针。所以,总结一下,修复方法如下:
This is extremely curious. I've ruled out the std vs. boost shared pointer possibility, and played around with a few sanity checks, and as far as I can tell it's something that boost python is doing to the shared pointer that breaks it.
Tracing object constructors / destructors, the lifetimes of Empty and Store are being managed as you would expect (no copies occur).
An extremely interesting thing is that
shared_from_this
continues to work, even whenweak_ptr<>.lock()
doesn't, and, in fact, a new weak pointer created from a new shared pointer (from shared_from_this) does work.So this lead me to the thread linked in the comments, it seems like there's something boost python is doing with the deleter and reference count that is breaking the weak pointer.
Inspecting the shared pointers in the debugger this is what we get:
When we call
setValue
, this is what the argument looks like:If we create a shared pointer using
shared_from_this
on the argument, it looks like this:There's one thing to note here: the address of the shared count is different: this is a different shared pointer instance... so we've somehow created two different shared pointers to the same address. This is an extremely bad thing, since we'd expect this to shortly result in a double-free.
However, it doesn't. (I'm quite keen to understand this further, if anyone has any ideas?)
I don't know why it doesn't, to be honest (presumably there is something subtle going on here), but in any case, all this does point to a solution: We can use
shared_from_this
to create a shared pointer, from the value passed, which we can use to create a weak pointer that actually works.So, to wrap up, here's the fix:
另一种解决方法是传入对
shared_ptr
的引用,而不是对shared_ptr
对象本身。在这种情况下,显然boost::python-Wrapper
并没有创建这个奇怪的单独的shared_ptr
。顺便提一句。如果你查看shared_ptr.pn.pi_指向的类的类型,你会发现分发给Python的“原始”
shared_ptr
包含一个指向sp_counted_impl_p的指针。
对象,而从 Python 传回的shared_ptr
包含一个指向sp_counted_impl_pd,
shared_ptr_deleter>对象
(请参阅smart_ptr/detail/sp_counted_impl.hpp
)pd 变体不包含对被指点的引用。我怀疑此
sp_counted_impl_pd
对象以某种方式引用sp_counted_impl_p
对象。这可以解释为什么当从 Python 传回的shared_ptr
的引用计数降至零时,析构函数不会被调用。事实上weak_ptr
在这种情况下不起作用可能只是一个错误......?Another workaround is to pass in a reference to the
shared_ptr
, not theshared_ptr
object itself. In that case obviouslyboost::python-Wrapper
is not creating this strange separateshared_ptr
.Btw. I you look at the type of the class pointed to by shared_ptr.pn.pi_ you'll see that the "original"
shared_ptr
handed out to Python contains a pointer to asp_counted_impl_p<POINTEE_TYPE>
object while theshared_ptr
passed back in from Python contains a pointer to asp_counted_impl_pd<void*
,shared_ptr_deleter> object
(seesmart_ptr/detail/sp_counted_impl.hpp
)The pd variant does not contain a reference to the pointee. I suspect that this
sp_counted_impl_pd
object is somehow referencing thesp_counted_impl_p
object. This would explain why the destructor is not called when the reference count of theshared_ptr
passed back in from Python drops to zero. The fact thatweak_ptr
does not work in this case might simply be a bug then..?