Skip to content

Commit fe6c6bf

Browse files
committed
added 'construct_custodian_for' and test
(fixes problem that 'with_custodian_and_ward_postcall' doesn't work in connection with 'make_constructor')
1 parent 832a1ed commit fe6c6bf

4 files changed

Lines changed: 131 additions & 1 deletion

File tree

include/boost/python/init.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,15 @@ class init : public init_base<init<BOOST_PYTHON_OVERLOAD_ARGS> >
266266
policies, this->doc_string(), this->keywords());
267267
}
268268

269+
template <std::size_t ward>
270+
init_with_call_policies<with_custodian_and_ward_postcall<1, ward+1>, self_t>
271+
operator[](construct_custodian_for<ward> const&) const
272+
{
273+
typedef with_custodian_and_ward_postcall<1, ward+1> init_policy;
274+
return init_with_call_policies<init_policy, self_t>(
275+
init_policy(), this->doc_string(), this->keywords());
276+
}
277+
269278
typedef detail::type_list<BOOST_PYTHON_OVERLOAD_ARGS> signature_;
270279

271280
typedef detail::is_optional<

include/boost/python/with_custodian_and_ward.hpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,49 @@ struct with_custodian_and_ward_postcall : BasePolicy_
119119
}
120120
};
121121

122+
template <std::size_t ward>
123+
struct construct_custodian_for : default_call_policies
124+
{
125+
BOOST_STATIC_ASSERT(ward > 0);
126+
127+
template <class ArgumentPackage>
128+
static PyObject* postcall(ArgumentPackage const& args_, PyObject* result)
129+
{
130+
std::size_t arity_ = detail::arity(args_);
131+
#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)
132+
if (ward > arity_ )
133+
#else
134+
// check if ward exceeds the arity
135+
// (this weird formulation avoids "always false" warnings
136+
// for arity_ = 0)
137+
if ( (std::max<std::size_t>)(0, ward) > arity_ )
138+
#endif
139+
{
140+
PyErr_SetString(
141+
PyExc_IndexError
142+
, "boost::python::construct_custodian_for: argument index out of range"
143+
);
144+
return 0;
145+
}
146+
147+
PyObject* patient = detail::get_prev<ward>::execute(args_, result);
148+
PyObject* nurse = detail::get_prev<1>::execute(args_.base);
149+
150+
if (nurse == 0) return 0;
151+
152+
result = default_call_policies::postcall(args_, result);
153+
if (result == 0)
154+
return 0;
155+
156+
if (python::objects::make_nurse_and_patient(nurse, patient) == 0)
157+
{
158+
Py_XDECREF(result);
159+
return 0;
160+
}
161+
return result;
162+
}
163+
};
164+
122165

123166
}} // namespace boost::python
124167

test/test_pointer_adoption.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <boost/python/manage_new_object.hpp>
99
#include <boost/python/return_internal_reference.hpp>
1010
#include <boost/python/class.hpp>
11+
#include <boost/python/make_constructor.hpp>
1112

1213
using namespace boost::python;
1314

@@ -86,6 +87,11 @@ A* as_A(Base* b)
8687
return dynamic_cast<A*>(b);
8788
}
8889

90+
B* create_B(A* a)
91+
{
92+
return new B(a);
93+
}
94+
8995
BOOST_PYTHON_MODULE(test_pointer_adoption_ext)
9096
{
9197
def("num_a_instances", num_a_instances);
@@ -102,6 +108,8 @@ BOOST_PYTHON_MODULE(test_pointer_adoption_ext)
102108
class_<A, bases<Base> >("A", no_init)
103109
.def("content", &A::content)
104110
.def("get_inner", &A::get_inner, return_internal_reference<>())
111+
.def("create_B", &create_B, with_custodian_and_ward_postcall<0,1,
112+
return_value_policy<manage_new_object> >())
105113
;
106114

107115
class_<inner>("inner", no_init)
@@ -120,6 +128,16 @@ BOOST_PYTHON_MODULE(test_pointer_adoption_ext)
120128

121129
.def("a_content", &B::a_content)
122130
;
131+
132+
class_<B>("B1")
133+
.def(init<A*>()[construct_custodian_for<1>()])
134+
.def("a_content", &B::a_content)
135+
;
136+
137+
class_<B>("B2")
138+
.def("__init__", make_constructor(&create_B, construct_custodian_for<1>()))
139+
.def("a_content", &B::a_content)
140+
;
123141
}
124142

125143
#include "module_tail.cpp"

test/test_pointer_adoption.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
44
"""
55
>>> from test_pointer_adoption_ext import *
6+
>>> import sys
67
78
>>> num_a_instances()
89
0
910
1011
>>> a = create('dynamically allocated')
1112
>>> num_a_instances()
1213
1
13-
14+
>>> sys.getrefcount(a)
15+
2
1416
>>> a.content()
1517
'dynamically allocated'
1618
@@ -55,13 +57,71 @@
5557
Test call policies for constructors here
5658
5759
>>> a = create('second a')
60+
>>> a.content()
61+
'second a'
62+
>>> num_a_instances()
63+
1
64+
>>> b = a.create_B()
65+
>>> num_a_instances()
66+
1
67+
>>> sys.getrefcount(a)
68+
3
69+
>>> b.a_content()
70+
'second a'
71+
>>> b = None
5872
>>> num_a_instances()
5973
1
74+
>>> sys.getrefcount(a)
75+
2
76+
6077
>>> b = B(a)
6178
>>> num_a_instances()
6279
1
80+
>>> sys.getrefcount(a)
81+
3
82+
>>> a.content()
83+
'second a'
84+
>>> b.a_content()
85+
'second a'
86+
>>> b = None
87+
>>> num_a_instances()
88+
1
89+
>>> sys.getrefcount(a)
90+
2
91+
92+
>>> b = B1(a)
93+
>>> num_a_instances()
94+
1
95+
>>> sys.getrefcount(a)
96+
3
97+
>>> a.content()
98+
'second a'
99+
>>> b.a_content()
100+
'second a'
101+
>>> b = None
102+
>>> num_a_instances()
103+
1
104+
>>> sys.getrefcount(a)
105+
2
106+
107+
>>> b = B2(a)
108+
>>> num_a_instances()
109+
1
110+
>>> sys.getrefcount(a)
111+
3
63112
>>> a.content()
64113
'second a'
114+
>>> b.a_content()
115+
'second a'
116+
>>> b = None
117+
>>> num_a_instances()
118+
1
119+
>>> sys.getrefcount(a)
120+
2
121+
122+
>>> b = B(a)
123+
>>> num_a_instances()
124+
1
65125
66126
>>> del a
67127
>>> num_a_instances()

0 commit comments

Comments
 (0)