Suppose I have the following two C++ classes (which I can't modify):
struct A
{
// stuff
};
struct B
{
// B will internally hold a reference to a
B(A& a, some_cpp_only_type arg);
};
I am trying to wrap the B class and hiding the some_cpp_only_type
argument from the Python interface (there is no alternative constructor for B
without this argument).
I have the following wrapper code right now:
using namespace boost::python;
boost::shared_ptr<B> make_B(A& a)
{
return boost::make_shared<B>(a, get_cpp_only_instance());
}
BOOST_PYTHON_MODULE(my_module)
{
class_<B, boost::noncopyable>("B", no_init)
.def("__init__", make_constructor(&make_B));
}
Right now this works because I'm holding a reference to the wrapped A
object inside of Python. However, I would really like to keep this around at least until the instance of B
is destroyed. I tried adding a with_custodian_and_ward_postcall
call policy to make_constructor
, but I get several pages of meaningless compiler errors (even from Clang). Here's the modified not working code:
using namespace boost::python;
boost::shared_ptr<B> make_B(A& a)
{
return boost::make_shared<B>(a, get_cpp_only_instance());
}
BOOST_PYTHON_MODULE(my_module)
{
class_<B, boost::noncopyable>("B", no_init)
.def("__init__", make_constructor(&make_B, with_custodian_and_ward_postcall<0,1>()));
}
How do I properly specify the call policy when using make_constructor
?
Alternative attempt using make_function
I tried using the solution Tanner Sansbury posted here of wrapping with make_function
instead, but this time even though the compile succeeds, I get a ValueError
from inside of Python:
No to_python (by-value) converter found for C++ type: A
For reference, here is the code I tried which uses make_function
(I get the same error with and without the custodian/ward call policy):
using namespace boost::python;
boost::shared_ptr<B> make_B(A& a)
{
return boost::make_shared<B>(a, get_cpp_only_instance());
}
void inter_make_B(object self, A& a)
{
auto constructor = make_constructor(&make_B);
constructor(self, a);
}
BOOST_PYTHON_MODULE(my_module)
{
class_<B, boost::noncopyable>("B", no_init)
.def("__init__", make_function(&inter_make_B, with_custodian_and_ward<1,2>()));
}
How do I properly write the wrapper for B
which manages memory correctly without modifying the B
class?
I know this post is 3 years old, but I was myself looking for this and unable to find any solution working out of the box with minimal modification of the original code. I finally managed to get it to work thanks to this other post.
Here is a mock based on the snippet you provided:
using namespace boost::python;
boost::shared_ptr<B> make_B(A& a)
{
return boost::make_shared<B>(a, get_cpp_only_instance());
}
void make_B_wrapper(object self, A& a)
{
auto constructor = make_constructor(&make_B);
constructor(self, a);
}
BOOST_PYTHON_MODULE(my_module)
{
class_<B, boost::noncopyable>("B", no_init)
.def("__init__", &make_B_wrapper, with_custodian_and_ward_postcall<1,2>());
}
And here is a complete working example demonstrating this approach:
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
namespace python = boost::python;
/// @brief Mockup class with verbose construction and destruction.
class Foo
{
public:
Foo() { std::cout << "Foo() " << this << std::endl; }
~Foo() { std::cout << "~Foo() " << this << std::endl; }
};
/// @brief Mockup class with verbose construction and destruction.
class Bar
{
public:
Bar() { std::cout << "Bar() " << this << std::endl; }
~Bar() { std::cout << "~Bar() " << this << std::endl; }
};
/// @brief Mockup factory function.
boost::shared_ptr<Foo> makeFoo(std::shared_ptr<Bar> & /* unused */)
{
return boost::make_shared<Foo>();
}
static void makeFooWrapper(python::object & self, std::shared_ptr<Bar> & bar)
{
auto constructor = python::make_constructor(&makeFoo);
constructor(self, bar);
}
BOOST_PYTHON_MODULE(example)
{
python::class_<Bar, std::shared_ptr<Bar>, boost::noncopyable>("Bar", python::init<>());
python::class_<Foo, std::shared_ptr<Foo>, boost::noncopyable>("Foo", python::no_init)
.def("__init__", &makeFooWrapper,
python::with_custodian_and_ward_postcall<1, 2>());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With