diff --git a/Jamfile b/Jamfile index e897b19f..ac32456b 100644 --- a/Jamfile +++ b/Jamfile @@ -20,6 +20,7 @@ PYTHON_PROPERTIES src/object/class.cpp src/object/function.cpp src/object/inheritance.cpp + src/object/life_support.cpp src/errors.cpp src/module.cpp src/objects.cpp diff --git a/include/boost/python/return_internal_reference.hpp b/include/boost/python/return_internal_reference.hpp index ac7f3fbf..a1888229 100644 --- a/include/boost/python/return_internal_reference.hpp +++ b/include/boost/python/return_internal_reference.hpp @@ -7,7 +7,10 @@ # define RETURN_INTERNAL_REFERENCE_DWA2002131_HPP # include +# include # include +# include +# include namespace boost { namespace python { @@ -28,18 +31,39 @@ struct internal_reference_to_python_generator { typedef typename mpl::select_type< !is_object::value - , internal_reference_to_python - , detail::return_internal_reference_requires_a_pointer_or_reference_return_type + , to_python_indirect + , detail::return_internal_reference_requires_a_pointer_or_reference_return_type >::type type; }; }; -template +template struct return_internal_reference : Base { - typedef wrap_internal_reference result_converter; + typedef reference_existing_object result_converter; + static PyObject* postcall(PyObject* args, PyObject* result); }; +template +PyObject* return_internal_reference::postcall(PyObject* args_, PyObject* result) +{ + PyObject* patient = PyTuple_GetItem(args_, owner_arg - 1); + if (patient != 0) // Make sure the argument was in range. + { + result = Base::postcall(args_,result); + if (result != 0) + { + if (python::objects::make_nurse_and_patient(result, patient) == 0) + { + return result; + } + } + + } + Py_XDECREF(result); + return 0; +} + }} // namespace boost::python #endif // RETURN_INTERNAL_REFERENCE_DWA2002131_HPP diff --git a/src/object/life_support.cpp b/src/object/life_support.cpp new file mode 100644 index 00000000..f587eb42 --- /dev/null +++ b/src/object/life_support.cpp @@ -0,0 +1,99 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#include +#include + +namespace boost { namespace python { namespace objects { + +struct life_support +{ + PyObject_HEAD + PyObject* patient; +}; + +extern "C" +{ + static void + life_support_dealloc(PyObject* self) + { + self->ob_type->tp_free(self); + } + + static PyObject * + life_support_call(PyObject *self, PyObject *arg, PyObject *kw) + { + // Let the patient die now + Py_XDECREF(((life_support*)self)->patient); + // Also let the weak reference die. This probably kills us. + Py_XDECREF(PyTuple_GET_ITEM(arg, 0)); + return detail::none(); + } +} + +PyTypeObject life_support_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "Boost.Python.life_support", + sizeof(life_support), + 0, + life_support_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, //(reprfunc)func_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + life_support_call, /* tp_call */ + 0, /* tp_str */ + 0, // PyObject_GenericGetAttr, /* tp_getattro */ + 0, // PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */ + 0, /* tp_doc */ + 0, // (traverseproc)func_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, //offsetof(PyLife_SupportObject, func_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, // func_memberlist, /* tp_members */ + 0, //func_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, //offsetof(PyLife_SupportObject, func_dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, + 0 /* tp_new */ +}; + +int make_nurse_and_patient(PyObject* nurse, PyObject* patient) +{ + life_support* system = PyObject_New(life_support, &life_support_type); + if (!system) + return -1; + + // We're going to leak this reference, but don't worry; the + // life_support system decrements it when the nurse dies. + PyObject* weakref = PyWeakref_NewRef(nurse, (PyObject*)system); + if (!weakref) + { + Py_XDECREF(system); + return -1; + } + + system->patient = patient; + Py_XINCREF(patient); // hang on to the patient until death + return 0; +} + +}}} // namespace boost::python::objects diff --git a/test/test_pointer_adoption.cpp b/test/test_pointer_adoption.cpp index be10aebd..61cd49bd 100644 --- a/test/test_pointer_adoption.cpp +++ b/test/test_pointer_adoption.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -75,7 +75,7 @@ BOOST_PYTHON_MODULE_INIT(test_pointer_adoption_ext) class_() .def("content", &A::content) - .def("get_inner", &A::get_inner, return_value_policy()) + .def("get_inner", &A::get_inner, return_internal_reference<>()) ) .add( diff --git a/test/test_pointer_adoption.py b/test/test_pointer_adoption.py index 197a6df9..28e160dc 100644 --- a/test/test_pointer_adoption.py +++ b/test/test_pointer_adoption.py @@ -16,10 +16,16 @@ >>> a.content() 'with an exposed reference' ->>> innards = None +# The a instance should be kept alive... >>> a = None >>> num_a_instances() +1 + +# ...until we're done with its innards +>>> innards = None +>>> num_a_instances() 0 + """ def run(args = None): import sys