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;
void create(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>
class C: public boost::noncopyable {};
std::vector<boost::python::object> v;
template <typename ...Args>
void create(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);
}
void register_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
>>> def func(c):
... global c2
... print 'func:', c
... c2 = c
...
>>> example.register_callback(func)
func: <example.C object at 0x7f12f425d0a8>
>>> assert(c1 is c2)