diff --git a/include/boost/python/converter/registered.hpp b/include/boost/python/converter/registered.hpp index 72982f48..56718a1a 100644 --- a/include/boost/python/converter/registered.hpp +++ b/include/boost/python/converter/registered.hpp @@ -10,8 +10,16 @@ # include # include # include +# include -namespace boost { namespace python { namespace converter { +namespace boost { + +// You'll see shared_ptr mentioned in this header because we need to +// note which types are shared_ptrs in their registrations, to +// implement special shared_ptr handling for rvalue conversions. +template class shared_ptr; + +namespace python { namespace converter { struct registration; @@ -26,9 +34,9 @@ namespace detail template struct registered - : detail::registered_base< + : detail::registered_base< typename add_reference< - typename add_cv::type + typename add_cv::type >::type > { @@ -50,10 +58,52 @@ struct registered // namespace detail { + inline void + register_shared_ptr(...) + { + } + + template + inline void + register_shared_ptr(type >) + { + registry::lookup_shared_ptr(type_id >()); + } + + template + inline void + register_shared_ptr(type const>) + { + detail::register_shared_ptr(type >()); + } + + template + inline void + register_shared_ptr(type volatile>) + { + detail::register_shared_ptr(type >()); + } + + template + inline void + register_shared_ptr(type const volatile>) + { + detail::register_shared_ptr(type >()); + } + + template + registration const& + registry_lookup(type) + { + detail::register_shared_ptr(type()); + return registry::lookup(type_id()); + } + template registration const& registered_base::converters - = registry::lookup(type_id()); + = detail::registry_lookup(type()); } + }}} // namespace boost::python::converter #endif // REGISTERED_DWA2002710_HPP diff --git a/include/boost/python/converter/registrations.hpp b/include/boost/python/converter/registrations.hpp index a18bec29..d71401de 100644 --- a/include/boost/python/converter/registrations.hpp +++ b/include/boost/python/converter/registrations.hpp @@ -33,7 +33,7 @@ struct rvalue_from_python_chain struct BOOST_PYTHON_DECL registration { public: // member functions - explicit registration(type_info); + explicit registration(type_info target, bool is_shared_ptr = false); // Convert the appropriately-typed data to Python PyObject* to_python(void const volatile*) const; @@ -56,7 +56,11 @@ struct BOOST_PYTHON_DECL registration // The unique to_python converter for the associated C++ type. to_python_function_t m_to_python; - + + // True iff this type is a shared_ptr. Needed for special rvalue + // from_python handling. + const bool is_shared_ptr; + # if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) private: void operator=(registration); // This is not defined, and just keeps MWCW happy. @@ -66,12 +70,13 @@ struct BOOST_PYTHON_DECL registration // // implementations // -inline registration::registration(type_info target_type) +inline registration::registration(type_info target_type, bool is_shared_ptr) : target_type(target_type) , lvalue_chain(0) , rvalue_chain(0) , m_class_object(0) , m_to_python(0) + , is_shared_ptr(is_shared_ptr) {} inline bool operator<(registration const& lhs, registration const& rhs) diff --git a/include/boost/python/converter/registry.hpp b/include/boost/python/converter/registry.hpp index 131cca72..5492edf7 100644 --- a/include/boost/python/converter/registry.hpp +++ b/include/boost/python/converter/registry.hpp @@ -20,6 +20,10 @@ namespace registry // Get the registration corresponding to the type, creating it if necessary BOOST_PYTHON_DECL registration const& lookup(type_info); + // Get the registration corresponding to the type, creating it if + // necessary. Use this first when the type is a shared_ptr. + BOOST_PYTHON_DECL registration const& lookup_shared_ptr(type_info); + // Return a pointer to the corresponding registration, if one exists BOOST_PYTHON_DECL registration const* query(type_info); diff --git a/include/boost/python/instance_holder.hpp b/include/boost/python/instance_holder.hpp index b8cabb92..0916348e 100755 --- a/include/boost/python/instance_holder.hpp +++ b/include/boost/python/instance_holder.hpp @@ -23,7 +23,13 @@ struct BOOST_PYTHON_DECL instance_holder : private noncopyable // return the next holder in a chain instance_holder* next() const; - virtual void* holds(type_info) = 0; + // When the derived holder actually holds by [smart] pointer and + // null_ptr_only is set, only report that the type is held when + // the pointer is null. This is needed for proper shared_ptr + // support, to prevent holding shared_ptrs from being found when + // converting from python so that we can use the conversion method + // that always holds the Python object. + virtual void* holds(type_info, bool null_ptr_only) = 0; void install(PyObject* inst) throw(); diff --git a/include/boost/python/object/find_instance.hpp b/include/boost/python/object/find_instance.hpp index 93a8e337..3202c1cd 100644 --- a/include/boost/python/object/find_instance.hpp +++ b/include/boost/python/object/find_instance.hpp @@ -10,8 +10,11 @@ namespace boost { namespace python { namespace objects { // Given a type_id, find the instance data which corresponds to it, or -// return 0 in case no such type is held. -BOOST_PYTHON_DECL void* find_instance_impl(PyObject*, type_info); +// return 0 in case no such type is held. If null_shared_ptr_only is +// true and the type being sought is a shared_ptr, only find an +// instance if it turns out to be NULL. Needed for shared_ptr rvalue +// from_python support. +BOOST_PYTHON_DECL void* find_instance_impl(PyObject*, type_info, bool null_shared_ptr_only = false); }}} // namespace boost::python::objects diff --git a/include/boost/python/object/pointer_holder.hpp b/include/boost/python/object/pointer_holder.hpp index d50c689d..b47f5e67 100644 --- a/include/boost/python/object/pointer_holder.hpp +++ b/include/boost/python/object/pointer_holder.hpp @@ -65,7 +65,7 @@ struct pointer_holder : instance_holder private: // types private: // required holder implementation - void* holds(type_info); + void* holds(type_info, bool null_ptr_only); template inline void* holds_wrapped(type_info dst_t, wrapper*,T* p) @@ -99,7 +99,7 @@ struct pointer_holder_back_reference : instance_holder # include BOOST_PP_ITERATE() private: // required holder implementation - void* holds(type_info); + void* holds(type_info, bool null_ptr_only); private: // data members Pointer m_p; @@ -120,9 +120,11 @@ inline pointer_holder_back_reference::pointer_holder_back_referen } template -void* pointer_holder::holds(type_info dst_t) +void* pointer_holder::holds(type_info dst_t, bool null_ptr_only) { - if (dst_t == python::type_id()) + if (dst_t == python::type_id() + && !(null_ptr_only && get_pointer(this->m_p)) + ) return &this->m_p; Value* p = get_pointer(this->m_p); @@ -137,10 +139,12 @@ void* pointer_holder::holds(type_info dst_t) } template -void* pointer_holder_back_reference::holds(type_info dst_t) +void* pointer_holder_back_reference::holds(type_info dst_t, bool null_ptr_only) { - if (dst_t == python::type_id()) - return &this->m_p; + if (dst_t == python::type_id() + && !(null_ptr_only && get_pointer(this->m_p)) + ) + return &this->m_p; if (!get_pointer(this->m_p)) return 0; diff --git a/include/boost/python/object/value_holder.hpp b/include/boost/python/object/value_holder.hpp index 9453bd9e..f7ba51bd 100644 --- a/include/boost/python/object/value_holder.hpp +++ b/include/boost/python/object/value_holder.hpp @@ -48,7 +48,7 @@ struct value_holder : instance_holder # include BOOST_PP_ITERATE() private: // required holder implementation - void* holds(type_info); + void* holds(type_info, bool null_ptr_only); template inline void* holds_wrapped(type_info dst_t, wrapper*,T* p) @@ -75,7 +75,7 @@ struct value_holder_back_reference : instance_holder # include BOOST_PP_ITERATE() private: // required holder implementation - void* holds(type_info); + void* holds(type_info, bool null_ptr_only); private: // data members Held m_held; @@ -84,7 +84,7 @@ private: // required holder implementation # undef BOOST_PYTHON_UNFORWARD_LOCAL template -void* value_holder::holds(type_info dst_t) +void* value_holder::holds(type_info dst_t, bool null_ptr_only) { if (void* wrapped = holds_wrapped(dst_t, &m_held, &m_held)) return wrapped; @@ -96,7 +96,7 @@ void* value_holder::holds(type_info dst_t) template void* value_holder_back_reference::holds( - type_info dst_t) + type_info dst_t, bool null_ptr_only) { type_info src_t = python::type_id(); Value* x = &m_held; diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index 4eb53f09..efe2bb19 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -43,7 +43,7 @@ BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1( // First check to see if it's embedded in an extension class // instance, as a special case. - data.convertible = objects::find_instance_impl(source, converters.target_type); + data.convertible = objects::find_instance_impl(source, converters.target_type, converters.is_shared_ptr); if (data.convertible) { data.construct = 0; diff --git a/src/converter/registry.cpp b/src/converter/registry.cpp index 86b499c5..71e7d456 100644 --- a/src/converter/registry.cpp +++ b/src/converter/registry.cpp @@ -128,7 +128,7 @@ namespace // } #endif // BOOST_PYTHON_CONVERTER_REGISTRY_APPLE_MACH_WORKAROUND - entry* get(type_info type) + entry* get(type_info type, bool is_shared_ptr = false) { # ifdef BOOST_PYTHON_TRACE_REGISTRY registry_t::iterator p = entries().find(entry(type)); @@ -138,7 +138,7 @@ namespace // ? "...NOT found\n" : "...found\n"); # endif std::pair pos_ins - = entries().insert(entry(type)); + = entries().insert(entry(type,is_shared_ptr)); # if __MWERKS__ >= 0x3000 // do a little invariant checking if a change was made @@ -230,6 +230,11 @@ namespace registry return *get(key); } + registration const& lookup_shared_ptr(type_info key) + { + return *get(key, true); + } + registration const* query(type_info type) { registry_t::iterator p = entries().find(entry(type)); diff --git a/src/object/class.cpp b/src/object/class.cpp index 191249e9..68642abc 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -407,7 +407,7 @@ namespace objects } BOOST_PYTHON_DECL void* - find_instance_impl(PyObject* inst, type_info type) + find_instance_impl(PyObject* inst, type_info type, bool null_shared_ptr_only) { if (inst->ob_type->ob_type != &class_metatype_object) return 0; @@ -416,7 +416,7 @@ namespace objects for (instance_holder* match = self->objects; match != 0; match = match->next()) { - void* const found = match->holds(type); + void* const found = match->holds(type, null_shared_ptr_only); if (found) return found; } diff --git a/test/Jamfile b/test/Jamfile index 089d0e47..7282d051 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -98,6 +98,7 @@ bpl-test crossmod_exception [ bpl-test return_arg ] [ bpl-test staticmethod ] [ bpl-test shared_ptr ] +[ bpl-test andreas_beyer ] [ bpl-test polymorphism ] [ bpl-test polymorphism2 ] [ bpl-test auto_ptr ] diff --git a/test/andreas_beyer.cpp b/test/andreas_beyer.cpp new file mode 100755 index 00000000..a39e9f04 --- /dev/null +++ b/test/andreas_beyer.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +using namespace boost; + +class A : public enable_shared_from_this { + public: + A() : val(0) {}; + int val; + typedef shared_ptr A_ptr; + A_ptr self() { + A_ptr self; + self = shared_from_this(); + return self; + } + +}; + +class B { + public: + B() { + a = A::A_ptr(new A()); + } + void set(A::A_ptr a) { + this->a = a; + } + A::A_ptr get() { + return a; + } + A::A_ptr a; +}; + +template +void hold_python(shared_ptr& x) +{ + x = python::extract >( python::object(x) ); +} + +A::A_ptr get_b_a(shared_ptr b) +{ + hold_python(b->a); + return b->get(); +} + +BOOST_PYTHON_MODULE(andreas_beyer_ext) { + python::class_ ("A") + .def("self", &A::self) + .def_readwrite("val", &A::val) + ; + python::register_ptr_to_python< A::A_ptr >(); + + python::class_("B") + .def("set", &B::set) +// .def("get", &B::get) + .def("get", get_b_a) + ; +} diff --git a/test/andreas_beyer.py b/test/andreas_beyer.py new file mode 100644 index 00000000..84a54fbd --- /dev/null +++ b/test/andreas_beyer.py @@ -0,0 +1,24 @@ +# Copyright David Abrahams 2004. Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +''' + >>> from andreas_beyer_ext import * + >>> b=B() + >>> a=b.get() # let b create an A + >>> a2=b.get() + >>> assert id(a) == id(a2) +''' +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + status = run()[0] + if (status == 0): print "Done." + sys.exit(status)