And*_*jos 5 c++ boost shared-ptr boost-python python-bindings
在使用Boost.Python和C++时,有时我们会创建使用类本身和boost::shared_ptr<>
版本绑定的类.由于许多原因,这非常方便,可以在很多地方使用.但是,当boost::python
返回一个boost::shared_ptr<>
在Python中生成并记录在C++静态变量上的值时,该机制似乎无法可靠地工作.
通常情况下,我希望返回的人会有boost::shared_ptr<>
一个特殊的删除器来处理这个问题,但事实似乎并非如此.似乎发生的事情是返回的boost::shared_ptr
只是包含一个指向Python中生成的值的指针,没有任何特殊考虑删除.这会导致双重删除(一个来自Python解释器本身,一个来自C++静态)的一致崩溃 - 或者至少看起来像它.
要使用下面的代码重现此行为,请创建如下所示的test.cc
文件并使用以下脚本进行测试.
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
struct A {
std::string _a;
A(std::string a): _a(a) {}
std::string str() { return _a; }
};
static boost::shared_ptr<A> holder(new A("foo"));
static void set_holder(boost::shared_ptr<A> a_ptr) {
holder = a_ptr;
}
static boost::shared_ptr<A> get_holder() {
return holder;
}
BOOST_PYTHON_MODULE(test)
{
using namespace boost::python;
class_<A, boost::shared_ptr<A> >("A", init<std::string>())
.def("__str__", &A::str)
;
def("set_holder", &set_holder);
def("get_holder", &get_holder);
}
Run Code Online (Sandbox Code Playgroud)
使用以下Python测试程序:
import test
print(str(test.get_holder()))
test.set_holder(test.A('bar'))
print(str(test.get_holder()))
Run Code Online (Sandbox Code Playgroud)
在Linux下编译(使用g++ -I/usr/include/python2.7 -shared -fpic test.cc -lboost_python -lpython2.7 -o test.so
)并运行上述程序(python test.py
)(ubuntu 12.10,使用Python 2.7和Boost 1.50),产生了以下堆栈跟踪:
#0 0x000000000048aae8 in ?? ()
#1 0x00007fa44f85f589 in boost::python::converter::shared_ptr_deleter::operator()(void const*) () from /usr/lib/libboost_python-py27.so.1.50.0
#2 0x00007fa44fa97cf9 in boost::detail::sp_counted_impl_pd<void*, boost::python::converter::shared_ptr_deleter>::dispose() ()
from /remote/filer.gx/home.active/aanjos/test.so
#3 0x00007fa44fa93f9c in boost::detail::sp_counted_base::release() ()
from /remote/filer.gx/home.active/aanjos/test.so
#4 0x00007fa44fa9402b in boost::detail::shared_count::~shared_count() ()
from /remote/filer.gx/home.active/aanjos/test.so
#5 0x00007fa44fa94404 in boost::shared_ptr<A>::~shared_ptr() ()
from /remote/filer.gx/home.active/aanjos/test.so
#6 0x00007fa450337901 in __run_exit_handlers (status=0,
listp=0x7fa4506b46a8 <__exit_funcs>, run_list_atexit=true) at exit.c:78
#7 0x00007fa450337985 in __GI_exit (status=<optimized out>) at exit.c:100
#8 0x00007fa45031d774 in __libc_start_main (main=0x44b769 <main>, argc=2,
ubp_av=0x7fffaa28e2a8, init=<optimized out>, fini=<optimized out>,
rtld_fini=<optimized out>, stack_end=0x7fffaa28e298) at libc-start.c:258
#9 0x00000000004ce6dd in _start ()
Run Code Online (Sandbox Code Playgroud)
这表示在静态析构函数中发生了双重删除.这种行为似乎在不同平台之间是一致的.
问题:是否有可能实现所描述的行为而无需复制返回的值boost::python
?在上面的玩具示例中,这很简单,但在我真正的问题中,深刻的副本A
将是不切实际的.
小智 1
你遇到的问题是在 python 完成后,shared_ptr 被破坏。看着:
\n\n我建议封装shared_ptr,它不需要额外的清理代码。\n不过有四个解决方案:
\n\n #include <boost/python.hpp>\n #include <boost/shared_ptr.hpp>\n #include <iostream>\n\nstruct A {\n std::string _a;\n A(std::string a): _a(a) {}\n ~A() { std::cout << "Destruct: " << _a << std::endl; }\n std::string str() { return _a; }\n};\n\nvoid python_exit();\n\nstatic boost::shared_ptr<A> holder(new A("foo"));\n\nstatic boost::shared_ptr<A> get_holder() {\n return holder;\n}\n\nstatic void set_holder(boost::shared_ptr<A> a_ptr) {\n // The shared_ptr comes with python::converter::shared_ptr_deleter\n holder = a_ptr;\n}\n\n\n// Fix 1: Cleanup while python is running\n// ======================================\n\nvoid reset_holder() {\n std::cout << "reset" << std::endl;\n holder.reset(new A("holder without shared_ptr_deleter"));\n}\n\n\n// Fix 2: The shared pointer is never deleted (which is a memory leak of a\n// global varialbe). The contained object is destructed, below.\n// =========================================================================\n\nstatic boost::shared_ptr<A>* holder_ptr = new boost::shared_ptr<A>(\n new A("foo_ptr"));\n\nstatic boost::shared_ptr<A> get_holder_ptr() {\n return *holder_ptr;\n}\n\nstatic void set_holder_ptr(boost::shared_ptr<A> a_ptr) {\n // Note: I know, it\'s no good to do that here (quick and dirty):\n Py_AtExit(python_exit);\n // The shared_ptr comes with python::converter::shared_ptr_deleter\n *holder_ptr = a_ptr;\n}\n\nvoid python_exit() {\n std::cout << "\\n"\n "Since Python\xe2\x80\x99s internal finalization will have completed before the\\n"\n "cleanup function, no Python APIs should be called in or after exit.\\n"\n "The boost::python::shared_ptr_deleter will do so, though.\\n"\n << std::endl;\n // Destruction but no deallocation.\n holder_ptr->get()->~A();\n}\n\n\n// Fix 3: Put a finalizer object into a module.\n// =========================================================================\n\nstatic boost::shared_ptr<A> holder_finalizer(new A("foo_finalizer"));\n\n\nstruct PythonModuleFinalizer\n{\n ~PythonModuleFinalizer();\n};\n\nPythonModuleFinalizer::~PythonModuleFinalizer() {\n std::cout << "PythonModuleFinalizer" << std::endl;\n holder_finalizer.reset(\n new A("holder_finalizer without shared_ptr_deleter"));\n}\n\nstatic boost::shared_ptr<A> get_holder_finalizer() {\n return holder_finalizer;\n}\n\nstatic void set_holder_finalizer(boost::shared_ptr<A> a_ptr) {\n // The shared_ptr comes with python::converter::shared_ptr_deleter\n holder_finalizer = a_ptr;\n}\n\n\n// Fix 4: Encapsulate the shared_ptr\n// =========================================================================\n\nclass B {\n private:\n struct I {\n std::string b;\n I(const std::string& b): b(b) {}\n ~I() { std::cout << "Destruct: " << b << std::endl; }\n };\n\n public:\n B(std::string b): s(new I(b)) {}\n std::string str() { return s.get()->b; }\n\n private:\n boost::shared_ptr<I> s;\n};\n\nstatic B holder_encapsulate("foo_encapsulate");\n\n\nstatic B get_holder_encapsulate() {\n return holder_encapsulate;\n}\n\nstatic void set_holder_encapsulate(B b) {\n holder_encapsulate = b;\n}\n\n\nBOOST_PYTHON_MODULE(test)\n{\n using namespace boost::python;\n\n class_<A, boost::shared_ptr<A> >("A", init<std::string>())\n .def("__str__", &A::str)\n ;\n\n def("set_holder", &set_holder);\n def("get_holder", &get_holder);\n def("reset_holder", &reset_holder);\n\n def("set_holder_ptr", &set_holder_ptr);\n def("get_holder_ptr", &get_holder_ptr);\n\n object finalizer_class = class_<PythonModuleFinalizer\n ("PythonModuleFinalizer", init<>());\n object finalizer = finalizer_class();\n scope().attr("ModuleFinalizer") = finalizer;\n def("set_holder_finalizer", &set_holder_finalizer);\n def("get_holder_finalizer", &get_holder_finalizer);\n\n class_<B>("B", init<std::string>())\n .def("__str__", &B::str)\n ;\n def("set_holder_encapsulate", &set_holder_encapsulate);\n def("get_holder_encapsulate", &get_holder_encapsulate);\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n蟒蛇文件:
\n\nimport test\nprint(str(test.get_holder()))\ntest.set_holder(test.A(\'bar\'))\nprint(str(test.get_holder()))\ntest.reset_holder()\n\nprint(str(test.get_holder_ptr()))\ntest.set_holder_ptr(test.A(\'bar_ptr\'))\nprint(str(test.get_holder_ptr()))\n\nprint(str(test.get_holder_finalizer()))\ntest.set_holder_finalizer(test.A(\'bar_finalizer\'))\nprint(str(test.get_holder_finalizer()))\n\nprint(str(test.get_holder_encapsulate()))\ntest.set_holder_encapsulate(test.B(\'bar_encapsulate\'))\nprint(str(test.get_holder_encapsulate()))\n
Run Code Online (Sandbox Code Playgroud)\n\n测试的输出是:
\n\nfoo\nDestruct: foo\nbar\nreset\nDestruct: bar\nfoo_ptr\nDestruct: foo_ptr\nbar_ptr\nfoo_finalizer\nDestruct: foo_finalizer\nbar_finalizer\nfoo_encapsulate\nDestruct: foo_encapsulate\nbar_encapsulate\nPythonModuleFinalizer\nDestruct: bar_finalizer\n\nSince Python\xe2\x80\x99s internal finalization will have completed before the\ncleanup function, no Python APIs should be called in or after exit.\nThe boost::python::shared_ptr_deleter will do so, though.\n\nDestruct: bar_ptr\nDestruct: bar_encapsulate\nDestruct: holder without shared_ptr_deleter\n
Run Code Online (Sandbox Code Playgroud)\n
归档时间: |
|
查看次数: |
501 次 |
最近记录: |