diff --git a/include/boost/python/init.hpp b/include/boost/python/init.hpp index 6598fd3547..0fed5c100b 100644 --- a/include/boost/python/init.hpp +++ b/include/boost/python/init.hpp @@ -266,6 +266,15 @@ class init : public init_base > policies, this->doc_string(), this->keywords()); } + template + init_with_call_policies, self_t> + operator[](construct_custodian_for const&) const + { + typedef with_custodian_and_ward_postcall<1, ward+1> init_policy; + return init_with_call_policies( + init_policy(), this->doc_string(), this->keywords()); + } + typedef detail::type_list signature_; typedef detail::is_optional< diff --git a/include/boost/python/with_custodian_and_ward.hpp b/include/boost/python/with_custodian_and_ward.hpp index 9399478f23..880d8ec38d 100644 --- a/include/boost/python/with_custodian_and_ward.hpp +++ b/include/boost/python/with_custodian_and_ward.hpp @@ -119,6 +119,49 @@ struct with_custodian_and_ward_postcall : BasePolicy_ } }; +template +struct construct_custodian_for : default_call_policies +{ + BOOST_STATIC_ASSERT(ward > 0); + + template + static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) + { + std::size_t arity_ = detail::arity(args_); +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + if (ward > arity_ ) +#else + // check if ward exceeds the arity + // (this weird formulation avoids "always false" warnings + // for arity_ = 0) + if ( (std::max)(0, ward) > arity_ ) +#endif + { + PyErr_SetString( + PyExc_IndexError + , "boost::python::construct_custodian_for: argument index out of range" + ); + return 0; + } + + PyObject* patient = detail::get_prev::execute(args_, result); + PyObject* nurse = detail::get_prev<1>::execute(args_.base); + + if (nurse == 0) return 0; + + result = default_call_policies::postcall(args_, result); + if (result == 0) + return 0; + + if (python::objects::make_nurse_and_patient(nurse, patient) == 0) + { + Py_XDECREF(result); + return 0; + } + return result; + } +}; + }} // namespace boost::python diff --git a/test/test_pointer_adoption.cpp b/test/test_pointer_adoption.cpp index a4e14af5d2..aa5e328248 100644 --- a/test/test_pointer_adoption.cpp +++ b/test/test_pointer_adoption.cpp @@ -8,6 +8,7 @@ #include #include #include +#include using namespace boost::python; @@ -86,6 +87,11 @@ A* as_A(Base* b) return dynamic_cast(b); } +B* create_B(A* a) +{ + return new B(a); +} + BOOST_PYTHON_MODULE(test_pointer_adoption_ext) { def("num_a_instances", num_a_instances); @@ -102,6 +108,8 @@ BOOST_PYTHON_MODULE(test_pointer_adoption_ext) class_ >("A", no_init) .def("content", &A::content) .def("get_inner", &A::get_inner, return_internal_reference<>()) + .def("create_B", &create_B, with_custodian_and_ward_postcall<0,1, + return_value_policy >()) ; class_("inner", no_init) @@ -120,6 +128,16 @@ BOOST_PYTHON_MODULE(test_pointer_adoption_ext) .def("a_content", &B::a_content) ; + + class_("B1") + .def(init()[construct_custodian_for<1>()]) + .def("a_content", &B::a_content) + ; + + class_("B2") + .def("__init__", make_constructor(&create_B, construct_custodian_for<1>())) + .def("a_content", &B::a_content) + ; } #include "module_tail.cpp" diff --git a/test/test_pointer_adoption.py b/test/test_pointer_adoption.py index 5bb9dd6dc3..79cab67d9f 100644 --- a/test/test_pointer_adoption.py +++ b/test/test_pointer_adoption.py @@ -3,6 +3,7 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) """ >>> from test_pointer_adoption_ext import * +>>> import sys >>> num_a_instances() 0 @@ -10,7 +11,8 @@ >>> a = create('dynamically allocated') >>> num_a_instances() 1 - +>>> sys.getrefcount(a) +2 >>> a.content() 'dynamically allocated' @@ -55,13 +57,71 @@ Test call policies for constructors here >>> a = create('second a') +>>> a.content() +'second a' +>>> num_a_instances() +1 +>>> b = a.create_B() +>>> num_a_instances() +1 +>>> sys.getrefcount(a) +3 +>>> b.a_content() +'second a' +>>> b = None >>> num_a_instances() 1 +>>> sys.getrefcount(a) +2 + >>> b = B(a) >>> num_a_instances() 1 +>>> sys.getrefcount(a) +3 +>>> a.content() +'second a' +>>> b.a_content() +'second a' +>>> b = None +>>> num_a_instances() +1 +>>> sys.getrefcount(a) +2 + +>>> b = B1(a) +>>> num_a_instances() +1 +>>> sys.getrefcount(a) +3 +>>> a.content() +'second a' +>>> b.a_content() +'second a' +>>> b = None +>>> num_a_instances() +1 +>>> sys.getrefcount(a) +2 + +>>> b = B2(a) +>>> num_a_instances() +1 +>>> sys.getrefcount(a) +3 >>> a.content() 'second a' +>>> b.a_content() +'second a' +>>> b = None +>>> num_a_instances() +1 +>>> sys.getrefcount(a) +2 + +>>> b = B(a) +>>> num_a_instances() +1 >>> del a >>> num_a_instances()