Skip to content

Commit 21d65ca

Browse files
committed
arbitrary argument/result adoption
[SVN r12664]
1 parent bcf4401 commit 21d65ca

7 files changed

Lines changed: 219 additions & 29 deletions

File tree

include/boost/python/object/life_support.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace boost { namespace python { namespace objects {
1212

13-
BOOST_PYTHON_DECL int make_nurse_and_patient(PyObject* nurse, PyObject* patient);
13+
BOOST_PYTHON_DECL PyObject* make_nurse_and_patient(PyObject* nurse, PyObject* patient);
1414

1515
}}} // namespace boost::python::object
1616

include/boost/python/object/make_holder.hpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,84 @@ struct make_holder<3>
9393
};
9494
};
9595

96+
template <>
97+
struct make_holder<4>
98+
{
99+
template <class T, class Generator, class ArgList>
100+
struct apply
101+
{
102+
typedef typename detail::eval<Generator,T>::type holder;
103+
typedef typename mpl::at<0,ArgList>::type t0;
104+
typedef typename forward<t0>::type f0;
105+
typedef typename mpl::at<1,ArgList>::type t1;
106+
typedef typename forward<t1>::type f1;
107+
typedef typename mpl::at<2,ArgList>::type t2;
108+
typedef typename forward<t2>::type f2;
109+
typedef typename mpl::at<3,ArgList>::type t3;
110+
typedef typename forward<t3>::type f3;
111+
112+
static void execute(
113+
PyObject* p, t0 a0, t1 a1, t2 a2, t3 a3)
114+
{
115+
(new holder(p, f0(a0), f1(a1), f2(a2), f3(a3)))->install(p);
116+
}
117+
};
118+
};
119+
120+
template <>
121+
struct make_holder<5>
122+
{
123+
template <class T, class Generator, class ArgList>
124+
struct apply
125+
{
126+
typedef typename detail::eval<Generator,T>::type holder;
127+
typedef typename mpl::at<0,ArgList>::type t0;
128+
typedef typename forward<t0>::type f0;
129+
typedef typename mpl::at<1,ArgList>::type t1;
130+
typedef typename forward<t1>::type f1;
131+
typedef typename mpl::at<2,ArgList>::type t2;
132+
typedef typename forward<t2>::type f2;
133+
typedef typename mpl::at<3,ArgList>::type t3;
134+
typedef typename forward<t3>::type f3;
135+
typedef typename mpl::at<4,ArgList>::type t4;
136+
typedef typename forward<t4>::type f4;
137+
138+
static void execute(
139+
PyObject* p, t0 a0, t1 a1, t2 a2, t3 a3, t4 a4)
140+
{
141+
(new holder(p, f0(a0), f1(a1), f2(a2), f3(a3), f4(a4)))->install(p);
142+
}
143+
};
144+
};
145+
146+
template <>
147+
struct make_holder<6>
148+
{
149+
template <class T, class Generator, class ArgList>
150+
struct apply
151+
{
152+
typedef typename detail::eval<Generator,T>::type holder;
153+
typedef typename mpl::at<0,ArgList>::type t0;
154+
typedef typename forward<t0>::type f0;
155+
typedef typename mpl::at<1,ArgList>::type t1;
156+
typedef typename forward<t1>::type f1;
157+
typedef typename mpl::at<2,ArgList>::type t2;
158+
typedef typename forward<t2>::type f2;
159+
typedef typename mpl::at<3,ArgList>::type t3;
160+
typedef typename forward<t3>::type f3;
161+
typedef typename mpl::at<4,ArgList>::type t4;
162+
typedef typename forward<t4>::type f4;
163+
typedef typename mpl::at<5,ArgList>::type t5;
164+
typedef typename forward<t5>::type f5;
165+
166+
static void execute(
167+
PyObject* p, t0 a0, t1 a1, t2 a2, t3 a3, t4 a4, t5 a5)
168+
{
169+
(new holder(p, f0(a0), f1(a1), f2(a2), f3(a3), f4(a4), f5(a5)))->install(p);
170+
}
171+
};
172+
};
173+
96174
}}} // namespace boost::python::objects
97175

98176
#endif // MAKE_HOLDER_DWA20011215_HPP

include/boost/python/return_internal_reference.hpp

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
# define RETURN_INTERNAL_REFERENCE_DWA2002131_HPP
88

99
# include <boost/python/default_call_policies.hpp>
10-
# include <boost/python/object/life_support.hpp>
1110
# include <boost/type_traits/object_traits.hpp>
1211
# include <boost/python/reference_existing_object.hpp>
1312
# include <boost/python/to_python_indirect.hpp>
13+
# include <boost/python/with_custodian_and_ward.hpp>
1414

1515
namespace boost { namespace python {
1616

@@ -38,32 +38,12 @@ struct internal_reference_to_python_generator
3838
};
3939

4040
template <std::size_t owner_arg = 1, class Base = default_call_policies>
41-
struct return_internal_reference : Base
41+
struct return_internal_reference
42+
: with_custodian_and_ward_postcall<0, owner_arg, Base>
4243
{
4344
typedef reference_existing_object result_converter;
44-
static PyObject* postcall(PyObject* args, PyObject* result);
4545
};
4646

47-
template <std::size_t owner_arg, class Base>
48-
PyObject* return_internal_reference<owner_arg,Base>::postcall(PyObject* args_, PyObject* result)
49-
{
50-
PyObject* patient = PyTuple_GetItem(args_, owner_arg - 1);
51-
if (patient != 0) // Make sure the argument was in range.
52-
{
53-
result = Base::postcall(args_,result);
54-
if (result != 0)
55-
{
56-
if (python::objects::make_nurse_and_patient(result, patient) == 0)
57-
{
58-
return result;
59-
}
60-
}
61-
62-
}
63-
Py_XDECREF(result);
64-
return 0;
65-
}
66-
6747
}} // namespace boost::python
6848

6949
#endif // RETURN_INTERNAL_REFERENCE_DWA2002131_HPP
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef WITH_CUSTODIAN_AND_WARD_DWA2002131_HPP
7+
# define WITH_CUSTODIAN_AND_WARD_DWA2002131_HPP
8+
9+
# include <boost/python/default_call_policies.hpp>
10+
# include <boost/python/object/life_support.hpp>
11+
12+
namespace boost { namespace python {
13+
14+
enum custodial_timing { pre_call, post_call };
15+
16+
template <std::size_t custodian, std::size_t ward, class Base = default_call_policies>
17+
struct with_custodian_and_ward : Base
18+
{
19+
static bool precall(PyObject* args);
20+
};
21+
22+
template <std::size_t custodian, std::size_t ward, class Base = default_call_policies>
23+
struct with_custodian_and_ward_postcall : Base
24+
{
25+
static PyObject* postcall(PyObject* args, PyObject* result);
26+
};
27+
28+
//
29+
// implementations
30+
//
31+
template <std::size_t custodian, std::size_t ward, class Base>
32+
bool with_custodian_and_ward<custodian,ward,Base>::precall(PyObject* args_)
33+
{
34+
BOOST_STATIC_ASSERT(custodian != ward);
35+
BOOST_STATIC_ASSERT(custodian > 0);
36+
BOOST_STATIC_ASSERT(ward > 0);
37+
38+
PyObject* patient = PyTuple_GetItem(args_, ward - 1);
39+
if (patient == 0) return false;
40+
PyObject* nurse = PyTuple_GetItem(args_, custodian - 1);
41+
if (nurse == 0) return false;
42+
43+
PyObject* life_support = python::objects::make_nurse_and_patient(nurse, patient);
44+
if (life_support == 0)
45+
return false;
46+
47+
bool result = Base::precall(args_);
48+
49+
if (!result)
50+
Py_XDECREF(life_support);
51+
52+
return result;
53+
}
54+
55+
template <std::size_t custodian, std::size_t ward, class Base>
56+
PyObject* with_custodian_and_ward_postcall<custodian,ward,Base>::postcall(PyObject* args_, PyObject* result)
57+
{
58+
BOOST_STATIC_ASSERT(custodian != ward);
59+
60+
PyObject* patient = ward > 0 ? PyTuple_GetItem(args_, ward - 1) : result;
61+
if (patient == 0) return 0;
62+
63+
PyObject* nurse = custodian > 0 ? PyTuple_GetItem(args_, custodian - 1) : result;
64+
if (nurse == 0) return 0;
65+
66+
result = Base::postcall(args_, result);
67+
if (result == 0)
68+
return 0;
69+
70+
if (python::objects::make_nurse_and_patient(nurse, patient) == 0)
71+
{
72+
Py_XDECREF(result);
73+
return 0;
74+
}
75+
return result;
76+
}
77+
78+
}} // namespace boost::python
79+
80+
#endif // WITH_CUSTODIAN_AND_WARD_DWA2002131_HPP

src/object/life_support.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern "C"
1919
static void
2020
life_support_dealloc(PyObject* self)
2121
{
22+
Py_XDECREF(((life_support*)self)->patient);
2223
self->ob_type->tp_free(self);
2324
}
2425

@@ -27,7 +28,8 @@ extern "C"
2728
{
2829
// Let the patient die now
2930
Py_XDECREF(((life_support*)self)->patient);
30-
// Also let the weak reference die. This probably kills us.
31+
((life_support*)self)->patient = 0;
32+
// Let the weak reference die. This probably kills us.
3133
Py_XDECREF(PyTuple_GET_ITEM(arg, 0));
3234
return detail::none();
3335
}
@@ -76,24 +78,24 @@ PyTypeObject life_support_type = {
7678
0 /* tp_new */
7779
};
7880

79-
int make_nurse_and_patient(PyObject* nurse, PyObject* patient)
81+
PyObject* make_nurse_and_patient(PyObject* nurse, PyObject* patient)
8082
{
8183
life_support* system = PyObject_New(life_support, &life_support_type);
8284
if (!system)
83-
return -1;
85+
return 0;
8486

8587
// We're going to leak this reference, but don't worry; the
8688
// life_support system decrements it when the nurse dies.
8789
PyObject* weakref = PyWeakref_NewRef(nurse, (PyObject*)system);
8890
if (!weakref)
8991
{
9092
Py_XDECREF(system);
91-
return -1;
93+
return 0;
9294
}
9395

9496
system->patient = patient;
9597
Py_XINCREF(patient); // hang on to the patient until death
96-
return 0;
98+
return weakref;
9799
}
98100

99101
}}} // namespace boost::python::objects

test/test_pointer_adoption.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ struct A
5757
inner x;
5858
};
5959

60+
struct B
61+
{
62+
B() : x(0) {}
63+
64+
inner const* adopt(A* x) { this->x = x; return &x->get_inner(); }
65+
66+
std::string a_content()
67+
{
68+
return x ? x->content() : std::string("empty");
69+
}
70+
71+
A* x;
72+
};
73+
74+
6075
A* create(std::string const& s)
6176
{
6277
return new A(s);
@@ -83,6 +98,19 @@ BOOST_PYTHON_MODULE_INIT(test_pointer_adoption_ext)
8398
.def("change", &inner::change)
8499
)
85100

101+
.add(
102+
class_<B>("B")
103+
.def_init()
104+
105+
.def("adopt", &B::adopt
106+
// Adopt returns a pointer referring to a subobject of its 2nd argument (1st being "self")
107+
, return_internal_reference<2
108+
// Meanwhile, self holds a reference to the 2nd argument.
109+
, with_custodian_and_ward<1,2> >()
110+
)
111+
112+
.def("a_content", &B::a_content)
113+
)
86114
;
87115
}
88116

test/test_pointer_adoption.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,28 @@
2626
>>> num_a_instances()
2727
0
2828
29+
>>> b = B()
30+
>>> a = create('another')
31+
>>> b.a_content()
32+
'empty'
33+
>>> innards = b.adopt(a);
34+
>>> b.a_content()
35+
'another'
36+
>>> num_a_instances()
37+
1
38+
>>> del a # innards and b are both holding a reference
39+
>>> num_a_instances()
40+
1
41+
>>> innards.change('yet another')
42+
>>> b.a_content()
43+
'yet another'
44+
45+
>>> del innards
46+
>>> num_a_instances() # b still owns a reference to a
47+
1
48+
>>> del b
49+
>>> num_a_instances()
50+
0
2951
"""
3052
def run(args = None):
3153
import sys

0 commit comments

Comments
 (0)