Skip to content

Commit 262396d

Browse files
committed
Object life support
[SVN r12662]
1 parent 0a9d5f6 commit 262396d

File tree

5 files changed

+137
-7
lines changed

5 files changed

+137
-7
lines changed

Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ PYTHON_PROPERTIES
2020
src/object/class.cpp
2121
src/object/function.cpp
2222
src/object/inheritance.cpp
23+
src/object/life_support.cpp
2324
src/errors.cpp
2425
src/module.cpp
2526
src/objects.cpp

include/boost/python/return_internal_reference.hpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +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>
1011
# include <boost/type_traits/object_traits.hpp>
12+
# include <boost/python/reference_existing_object.hpp>
13+
# include <boost/python/to_python_indirect.hpp>
1114

1215
namespace boost { namespace python {
1316

@@ -28,18 +31,39 @@ struct internal_reference_to_python_generator
2831
{
2932
typedef typename mpl::select_type<
3033
!is_object<T>::value
31-
, internal_reference_to_python<T>
32-
, detail::return_internal_reference_requires_a_pointer_or_reference_return_type
34+
, to_python_indirect<T, detail::make_reference_holder>
35+
, detail::return_internal_reference_requires_a_pointer_or_reference_return_type<T>
3336
>::type type;
3437
};
3538
};
3639

37-
template <std::size_t owner_arg, class Base = default_call_policies>
40+
template <std::size_t owner_arg = 1, class Base = default_call_policies>
3841
struct return_internal_reference : Base
3942
{
40-
typedef wrap_internal_reference<owner_arg> result_converter;
43+
typedef reference_existing_object result_converter;
44+
static PyObject* postcall(PyObject* args, PyObject* result);
4145
};
4246

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+
4367
}} // namespace boost::python
4468

4569
#endif // RETURN_INTERNAL_REFERENCE_DWA2002131_HPP

src/object/life_support.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
#include <boost/python/object/life_support.hpp>
7+
#include <boost/python/detail/none.hpp>
8+
9+
namespace boost { namespace python { namespace objects {
10+
11+
struct life_support
12+
{
13+
PyObject_HEAD
14+
PyObject* patient;
15+
};
16+
17+
extern "C"
18+
{
19+
static void
20+
life_support_dealloc(PyObject* self)
21+
{
22+
self->ob_type->tp_free(self);
23+
}
24+
25+
static PyObject *
26+
life_support_call(PyObject *self, PyObject *arg, PyObject *kw)
27+
{
28+
// Let the patient die now
29+
Py_XDECREF(((life_support*)self)->patient);
30+
// Also let the weak reference die. This probably kills us.
31+
Py_XDECREF(PyTuple_GET_ITEM(arg, 0));
32+
return detail::none();
33+
}
34+
}
35+
36+
PyTypeObject life_support_type = {
37+
PyObject_HEAD_INIT(&PyType_Type)
38+
0,
39+
"Boost.Python.life_support",
40+
sizeof(life_support),
41+
0,
42+
life_support_dealloc, /* tp_dealloc */
43+
0, /* tp_print */
44+
0, /* tp_getattr */
45+
0, /* tp_setattr */
46+
0, /* tp_compare */
47+
0, //(reprfunc)func_repr, /* tp_repr */
48+
0, /* tp_as_number */
49+
0, /* tp_as_sequence */
50+
0, /* tp_as_mapping */
51+
0, /* tp_hash */
52+
life_support_call, /* tp_call */
53+
0, /* tp_str */
54+
0, // PyObject_GenericGetAttr, /* tp_getattro */
55+
0, // PyObject_GenericSetAttr, /* tp_setattro */
56+
0, /* tp_as_buffer */
57+
Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */
58+
0, /* tp_doc */
59+
0, // (traverseproc)func_traverse, /* tp_traverse */
60+
0, /* tp_clear */
61+
0, /* tp_richcompare */
62+
0, //offsetof(PyLife_SupportObject, func_weakreflist), /* tp_weaklistoffset */
63+
0, /* tp_iter */
64+
0, /* tp_iternext */
65+
0, /* tp_methods */
66+
0, // func_memberlist, /* tp_members */
67+
0, //func_getsetlist, /* tp_getset */
68+
0, /* tp_base */
69+
0, /* tp_dict */
70+
0, /* tp_descr_get */
71+
0, /* tp_descr_set */
72+
0, //offsetof(PyLife_SupportObject, func_dict), /* tp_dictoffset */
73+
0, /* tp_init */
74+
0, /* tp_alloc */
75+
0,
76+
0 /* tp_new */
77+
};
78+
79+
int make_nurse_and_patient(PyObject* nurse, PyObject* patient)
80+
{
81+
life_support* system = PyObject_New(life_support, &life_support_type);
82+
if (!system)
83+
return -1;
84+
85+
// We're going to leak this reference, but don't worry; the
86+
// life_support system decrements it when the nurse dies.
87+
PyObject* weakref = PyWeakref_NewRef(nurse, (PyObject*)system);
88+
if (!weakref)
89+
{
90+
Py_XDECREF(system);
91+
return -1;
92+
}
93+
94+
system->patient = patient;
95+
Py_XINCREF(patient); // hang on to the patient until death
96+
return 0;
97+
}
98+
99+
}}} // namespace boost::python::objects

test/test_pointer_adoption.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <boost/python/module.hpp>
77
#include <boost/python/return_value_policy.hpp>
88
#include <boost/python/manage_new_object.hpp>
9-
#include <boost/python/reference_existing_object.hpp>
9+
#include <boost/python/return_internal_reference.hpp>
1010
#include <boost/python/class.hpp>
1111
#include <boost/mpl/type_list.hpp>
1212

@@ -75,7 +75,7 @@ BOOST_PYTHON_MODULE_INIT(test_pointer_adoption_ext)
7575

7676
class_<A>()
7777
.def("content", &A::content)
78-
.def("get_inner", &A::get_inner, return_value_policy<reference_existing_object>())
78+
.def("get_inner", &A::get_inner, return_internal_reference<>())
7979
)
8080

8181
.add(

test/test_pointer_adoption.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@
1616
>>> a.content()
1717
'with an exposed reference'
1818
19-
>>> innards = None
19+
# The a instance should be kept alive...
2020
>>> a = None
2121
>>> num_a_instances()
22+
1
23+
24+
# ...until we're done with its innards
25+
>>> innards = None
26+
>>> num_a_instances()
2227
0
28+
2329
"""
2430
def run(args = None):
2531
import sys

0 commit comments

Comments
 (0)