Boost Python Return Same Instance With Make_constructor
Solution 1:
When Python constructs an object, the __init__
will be invoked with the first argument representing the object being constructed, often called self
. Boost.Python tries to hide this argument as much as possible, only exposing it to an init-expression under certain conditions. The callable Python object returned from boost::python::make_constructor()
is aware of this first argument, but there are no customization points to have it forward the argument to wrapped function. One solution is to expose a C++ function as __init__
with boost::python::make_function()
that accepts all of the arguments to be provided from Python, including self
, then delegate to the functor returned from boost::python::make_constructor()
:
...
std::vector<boost::python::object> v;
voidcreate(boost::python::object self){
// Create a constructor object. In this case, a lambda// casted to a function is being used.auto constructor = boost::python::make_constructor(+[]() {
return boost::make_shared<C>();
});
// Invoke the constructor.constructor(self);
// If construction does not throw, then store a reference to self.
v.push_back(self);
}
...
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<C, boost::shared_ptr<C>, boost::noncopyable>(
"C", python::no_init)
.def("__init__", python::make_function(&create))
;
...
}
Here is a complete example demonstrating this approach:
#include<boost/python.hpp>#include<vector>#include<boost/make_shared.hpp>classC: public boost::noncopyable {};
std::vector<boost::python::object> v;
template <typename ...Args>
voidcreate(boost::python::object self, Args&&... args){
// Create a constructor object.auto constructor = boost::python::make_constructor(
+[](Args&&...args) {
return boost::make_shared<C>(std::forward<Args>(args)...);
});
// Invoke the constructor.constructor(self, std::forward<Args>(args)...);
// If construction does not throw, then store a reference to self.
v.push_back(self);
}
voidregister_callback(boost::python::object func){
func(v[0]);
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<C, boost::shared_ptr<C>, boost::noncopyable>(
"C", python::no_init)
.def("__init__", python::make_function(&create<>))
;
python::def("register_callback", ®ister_callback);
}
Interactive usage:
>>> import example
>>> c1 = example.C()
>>> print'init:', c1
init: <example.C object at 0x7f12f425d0a8>
>>> c2 = None>>> deffunc(c):
... global c2
... print'func:', c
... c2 = c
...
>>> example.register_callback(func)
func: <example.C object at 0x7f12f425d0a8>
>>> assert(c1 is c2)
Solution 2:
It already is the same C
instance inside of the same shared_ptr<C>
. You can easily verify this.
The issue is that you're creating two different py::object
s based on this shared_ptr<C>
: one from create()
and one from register_callback()
. Those are necessarily going to be different objects, they have nothing to do with each other.
The simple solution is to just not use make_constructor
. Create a factory function:
py::objectmakeC() {
C *c = newC();
auto ptr = py::object{boost::shared_ptr<C>(c)};
v.push_back(ptr); // store as objects, since you need them to be the samereturn ptr;
}
voidregister_callback(object func) {
func(v[0]);
}
BOOST_PYTHON_MODULE(test1)
{
class_<C, boost::shared_ptr<C>, boost::noncopyable>("C", no_init)
/* actually no_init for real */
;
def("makeC", makeC);
def("register_callback", register_callback);
}
Post a Comment for "Boost Python Return Same Instance With Make_constructor"