From c8e8ccfa22a888e4f7930f80814691c35e6c2408 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 26 Jul 2004 15:34:36 +0000 Subject: [PATCH] New-style polymorphism [SVN r24083] --- build/Jamfile | 1 + include/boost/python/base_type_traits.hpp | 6 + include/boost/python/class.hpp | 79 +++++---- .../python/converter/return_from_python.hpp | 2 + include/boost/python/detail/enable_if.hpp | 70 ++++++++ include/boost/python/detail/is_wrapper.hpp | 29 ++++ include/boost/python/detail/sfinae.hpp | 13 ++ include/boost/python/detail/unwind_type.hpp | 150 ----------------- .../boost/python/detail/unwrap_type_id.hpp | 31 ++++ .../boost/python/detail/unwrap_wrapper.hpp | 48 ++++++ include/boost/python/detail/wrapper_base.hpp | 86 ++++++++++ include/boost/python/extract.hpp | 15 +- .../boost/python/object/class_metadata.hpp | 76 ++++++--- .../boost/python/object/pointer_holder.hpp | 33 +++- include/boost/python/object/value_holder.hpp | 22 ++- include/boost/python/override.hpp | 132 +++++++++++++++ include/boost/python/to_python_indirect.hpp | 108 ++++++------ include/boost/python/wrapper.hpp | 30 ++++ src/object/life_support.cpp | 2 +- src/wrapper.cpp | 65 +++++++ test/Jamfile | 1 + test/embedding.cpp | 22 +-- test/polymorphism2.cpp | 159 ++++++++++++++++++ test/polymorphism2.py | 71 ++++++++ 24 files changed, 957 insertions(+), 294 deletions(-) create mode 100755 include/boost/python/detail/enable_if.hpp create mode 100755 include/boost/python/detail/is_wrapper.hpp create mode 100755 include/boost/python/detail/sfinae.hpp delete mode 100644 include/boost/python/detail/unwind_type.hpp create mode 100755 include/boost/python/detail/unwrap_type_id.hpp create mode 100755 include/boost/python/detail/unwrap_wrapper.hpp create mode 100755 include/boost/python/detail/wrapper_base.hpp create mode 100755 include/boost/python/override.hpp create mode 100755 include/boost/python/wrapper.hpp create mode 100755 src/wrapper.cpp create mode 100755 test/polymorphism2.cpp create mode 100644 test/polymorphism2.py diff --git a/build/Jamfile b/build/Jamfile index 428f161c..59dd3912 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -53,6 +53,7 @@ if [ check-python-config ] object/iterator.cpp object_protocol.cpp object_operators.cpp + wrapper.cpp ; dll boost_python diff --git a/include/boost/python/base_type_traits.hpp b/include/boost/python/base_type_traits.hpp index 12322539..e1c6a484 100755 --- a/include/boost/python/base_type_traits.hpp +++ b/include/boost/python/base_type_traits.hpp @@ -32,6 +32,12 @@ struct base_type_traits typedef PyObject type; }; +template <> +struct base_type_traits +{ + typedef PyObject type; +}; + }} // namespace boost::python #endif // BASE_TYPE_TRAITS_DWA2002614_HPP diff --git a/include/boost/python/class.hpp b/include/boost/python/class.hpp index d5c0de6a..b98435be 100644 --- a/include/boost/python/class.hpp +++ b/include/boost/python/class.hpp @@ -29,6 +29,8 @@ # include # include # include +# include +# include # include # include @@ -151,7 +153,7 @@ namespace detail // This is the primary mechanism through which users will expose // C++ classes to Python. template < - class T // class being wrapped + class W // class being wrapped , class X1 // = detail::not_specified , class X2 // = detail::not_specified , class X3 // = detail::not_specified @@ -160,9 +162,9 @@ class class_ : public objects::class_base { public: // types typedef objects::class_base base; - typedef class_ self; - typedef typename objects::class_metadata metadata; - typedef T wrapped_type; + typedef class_ self; + typedef typename objects::class_metadata metadata; + typedef W wrapped_type; private: // types @@ -175,7 +177,7 @@ class class_ : public objects::class_base id_vector() { // Stick the derived class id into the first element of the array - ids[0] = type_id(); + ids[0] = detail::unwrap_type_id((W*)0, (W*)0); // Write the rest of the elements into succeeding positions. type_info* p = ids + 1; @@ -231,7 +233,9 @@ class class_ : public objects::class_base template self& def(char const* name, F f) { - this->def_impl(name, f, detail::def_helper(0), &f); + this->def_impl( + detail::unwrap_wrapper((W*)0) + , name, f, detail::def_helper(0), &f); return *this; } @@ -250,9 +254,10 @@ class class_ : public objects::class_base // def(name, function, doc_string, policy) this->def_impl( - name, fn - , detail::def_helper(a1,a2) - , &fn); + detail::unwrap_wrapper((W*)0) + , name, fn + , detail::def_helper(a1,a2) + , &fn); return *this; } @@ -261,9 +266,10 @@ class class_ : public objects::class_base self& def(char const* name, Fn fn, A1 const& a1, A2 const& a2, A3 const& a3) { this->def_impl( - name, fn - , detail::def_helper(a1,a2,a3) - , &fn); + detail::unwrap_wrapper((W*)0) + , name, fn + , detail::def_helper(a1,a2,a3) + , &fn); return *this; } @@ -371,7 +377,8 @@ class class_ : public objects::class_base typedef typename api::is_object_operators::type is_obj_or_proxy; return this->make_fn_impl( - f, is_obj_or_proxy(), (char*)0, detail::is_data_member_pointer() + detail::unwrap_wrapper((W*)0) + , f, is_obj_or_proxy(), (char*)0, detail::is_data_member_pointer() ); } @@ -381,32 +388,33 @@ class class_ : public objects::class_base typedef typename api::is_object_operators::type is_obj_or_proxy; return this->make_fn_impl( - f, is_obj_or_proxy(), (int*)0, detail::is_data_member_pointer() + detail::unwrap_wrapper((W*)0) + , f, is_obj_or_proxy(), (int*)0, detail::is_data_member_pointer() ); } - template - object make_fn_impl(F const& f, mpl::false_, void*, mpl::false_) + template + object make_fn_impl(T*, F const& f, mpl::false_, void*, mpl::false_) { return python::make_function(f, default_call_policies(), detail::get_signature(f, (T*)0)); } - template - object make_fn_impl(D B::*pm_, mpl::false_, char*, mpl::true_) + template + object make_fn_impl(T*, D B::*pm_, mpl::false_, char*, mpl::true_) { D T::*pm = pm_; return python::make_getter(pm); } - template - object make_fn_impl(D B::*pm_, mpl::false_, int*, mpl::true_) + template + object make_fn_impl(T*, D B::*pm_, mpl::false_, int*, mpl::true_) { D T::*pm = pm_; return python::make_setter(pm); } - template - object make_fn_impl(F const& x, mpl::true_, void*, mpl::false_) + template + object make_fn_impl(T*, F const& x, mpl::true_, void*, mpl::false_) { return x; } @@ -462,9 +470,10 @@ class class_ : public objects::class_base // generic visitor and everything else. // // @group def_impl { - template + template inline void def_impl( - char const* name + T* + , char const* name , LeafVisitor , Helper const& helper , def_visitor const* v @@ -473,9 +482,10 @@ class class_ : public objects::class_base v->visit(*this, name, helper); } - template + template inline void def_impl( - char const* name + T* + , char const* name , Fn fn , Helper const& helper , ... @@ -510,7 +520,7 @@ class class_ : public objects::class_base , Helper const& helper , mpl::bool_) { - detail::error::virtual_function_default::must_be_derived_class_member( + detail::error::virtual_function_default::must_be_derived_class_member( helper.default_implementation()); objects::add_to_namespace( @@ -554,7 +564,8 @@ class class_ : public objects::class_base , ...) { this->def_impl( - name + detail::unwrap_wrapper((W*)0) + , name , fn , detail::def_helper(a1) , &fn @@ -569,23 +580,23 @@ class class_ : public objects::class_base // implementations // -template -inline class_::class_(char const* name, char const* doc) +template +inline class_::class_(char const* name, char const* doc) : base(name, id_vector::size, id_vector().ids, doc) { this->initialize(init<>()); // select_holder::assert_default_constructible(); } -template -inline class_::class_(char const* name, no_init_t) +template +inline class_::class_(char const* name, no_init_t) : base(name, id_vector::size, id_vector().ids) { this->initialize(no_init); } -template -inline class_::class_(char const* name, char const* doc, no_init_t) +template +inline class_::class_(char const* name, char const* doc, no_init_t) : base(name, id_vector::size, id_vector().ids, doc) { this->initialize(no_init); diff --git a/include/boost/python/converter/return_from_python.hpp b/include/boost/python/converter/return_from_python.hpp index 999e1151..5db97485 100755 --- a/include/boost/python/converter/return_from_python.hpp +++ b/include/boost/python/converter/return_from_python.hpp @@ -9,9 +9,11 @@ # include # include # include +# include # include # include # include +# include # include # include # include diff --git a/include/boost/python/detail/enable_if.hpp b/include/boost/python/detail/enable_if.hpp new file mode 100755 index 00000000..247b06f2 --- /dev/null +++ b/include/boost/python/detail/enable_if.hpp @@ -0,0 +1,70 @@ +// 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) +#ifndef ENABLE_IF_DWA2004722_HPP +# define ENABLE_IF_DWA2004722_HPP + +# include +# include + +# if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) +# include + +namespace boost { namespace python { namespace detail { + +template +struct enable_if_arg +{ + typedef typename mpl::if_::type type; +}; + +template +struct disable_if_arg +{ + typedef typename mpl::if_::type type; +}; + +template +struct enable_if_ret +{ + typedef typename mpl::if_::type type; +}; + +template +struct disable_if_ret +{ + typedef typename mpl::if_::type type; +}; + +}}} // namespace boost::python::detail + +# elif !defined(BOOST_NO_SFINAE) +# include + +namespace boost { namespace python { namespace detail { + +template +struct enable_if_arg + : enable_if +{}; + +template +struct disable_if_arg + : disable_if +{}; + +template +struct enable_if_ret + : enable_if +{}; + +template +struct disable_if_ret + : disable_if +{}; + +}}} // namespace boost::python::detail + +# endif + +#endif // ENABLE_IF_DWA2004722_HPP diff --git a/include/boost/python/detail/is_wrapper.hpp b/include/boost/python/detail/is_wrapper.hpp new file mode 100755 index 00000000..d7bce7b6 --- /dev/null +++ b/include/boost/python/detail/is_wrapper.hpp @@ -0,0 +1,29 @@ +// 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) +#ifndef IS_WRAPPER_DWA2004723_HPP +# define IS_WRAPPER_DWA2004723_HPP + +# include +# include + +namespace boost { namespace python { + +template class wrapper; + +namespace detail +{ + typedef char (&is_not_wrapper)[2]; + is_not_wrapper is_wrapper_helper(...); + template + char is_wrapper_helper(wrapper const volatile*); + + // A metafunction returning true iff T is [derived from] wrapper + template + struct is_wrapper + : mpl::bool_<(sizeof(detail::is_wrapper_helper((T*)0)) == 1)> + {}; + +}}} // namespace boost::python::detail + +#endif // IS_WRAPPER_DWA2004723_HPP diff --git a/include/boost/python/detail/sfinae.hpp b/include/boost/python/detail/sfinae.hpp new file mode 100755 index 00000000..62818751 --- /dev/null +++ b/include/boost/python/detail/sfinae.hpp @@ -0,0 +1,13 @@ +// 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) +#ifndef SFINAE_DWA2004723_HPP +# define SFINAE_DWA2004723_HPP + +# include + +# if defined(BOOST_NO_SFINAE) && !defined(BOOST_MSVC) +# define BOOST_PYTHON_NO_SFINAE +# endif + +#endif // SFINAE_DWA2004723_HPP diff --git a/include/boost/python/detail/unwind_type.hpp b/include/boost/python/detail/unwind_type.hpp deleted file mode 100644 index 5d6a9b84..00000000 --- a/include/boost/python/detail/unwind_type.hpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright David Abrahams 2002. -// 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) -#ifndef UNWIND_TYPE_DWA200222_HPP -# define UNWIND_TYPE_DWA200222_HPP - -# include -# include -# include - -namespace boost { namespace python { namespace detail { - -template -inline typename Generator::result_type -unwind_type_cv(U* p, cv_unqualified, Generator* = 0) -{ - return Generator::execute(p); -} - -template -inline typename Generator::result_type -unwind_type_cv(U const* p, const_, Generator* = 0) -{ - return unwind_type(const_cast(p), (Generator*)0); -} - -template -inline typename Generator::result_type -unwind_type_cv(U volatile* p, volatile_, Generator* = 0) -{ - return unwind_type(const_cast(p), (Generator*)0); -} - -template -inline typename Generator::result_type -unwind_type_cv(U const volatile* p, const_volatile_, Generator* = 0) -{ - return unwind_type(const_cast(p), (Generator*)0); -} - -template -inline typename Generator::result_type -unwind_ptr_type(U* p, Generator* = 0) -{ - typedef typename cv_category::type tag; - return unwind_type_cv(p, tag()); -} - -template -struct unwind_helper -{ - template - static typename Generator::result_type - execute(U p, Generator* = 0) - { - return unwind_ptr_type(p, (Generator*)0); - } -}; - -template <> -struct unwind_helper -{ - template - static typename Generator::result_type - execute(U& p, Generator* = 0) - { - return unwind_ptr_type(&p, (Generator*)0); - } -}; - -template -inline typename Generator::result_type -unwind_type(U const& p, Generator* = 0) -{ - return unwind_helper::value>::execute(p, (Generator*)0); -} - -enum { direct_ = 0, pointer_ = 1, reference_ = 2, reference_to_pointer_ = 3 }; -template struct unwind_helper2; - -template <> -struct unwind_helper2 -{ - template - static typename Generator::result_type - execute(U(*)(), Generator* = 0) - { - return unwind_ptr_type((U*)0, (Generator*)0); - } -}; - -template <> -struct unwind_helper2 -{ - template - static typename Generator::result_type - execute(U*(*)(), Generator* = 0) - { - return unwind_ptr_type((U*)0, (Generator*)0); - } -}; - -template <> -struct unwind_helper2 -{ - template - static typename Generator::result_type - execute(U&(*)(), Generator* = 0) - { - return unwind_ptr_type((U*)0, (Generator*)0); - } -}; - -template <> -struct unwind_helper2 -{ - template - static typename Generator::result_type - execute(U&(*)(), Generator* = 0) - { - return unwind_ptr_type(U(0), (Generator*)0); - } -}; - -// Call this one with both template parameters explicitly specified -// and no function arguments: -// -// return unwind_type(); -// -// Doesn't work if T is an array type; we could handle that case, but -// why bother? -template -inline typename Generator::result_type -unwind_type(boost::type*p = 0, Generator* = 0) -{ - BOOST_STATIC_CONSTANT(int, indirection - = (is_pointer::value ? pointer_ : 0) - + (is_reference_to_pointer::value - ? reference_to_pointer_ - : is_reference::value - ? reference_ - : 0)); - - return unwind_helper2::execute((U(*)())0,(Generator*)0); -} - -}}} // namespace boost::python::detail - -#endif // UNWIND_TYPE_DWA200222_HPP diff --git a/include/boost/python/detail/unwrap_type_id.hpp b/include/boost/python/detail/unwrap_type_id.hpp new file mode 100755 index 00000000..96c1f530 --- /dev/null +++ b/include/boost/python/detail/unwrap_type_id.hpp @@ -0,0 +1,31 @@ +// 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) +#ifndef UNWRAP_TYPE_ID_DWA2004722_HPP +# define UNWRAP_TYPE_ID_DWA2004722_HPP + +# include + +# include + +namespace boost { namespace python { + +template class wrapper; + +namespace detail { + +template +inline type_info unwrap_type_id(T*, ...) +{ + return type_id(); +} + +template +inline type_info unwrap_type_id(U*, wrapper*) +{ + return type_id(); +} + +}}} // namespace boost::python::detail + +#endif // UNWRAP_TYPE_ID_DWA2004722_HPP diff --git a/include/boost/python/detail/unwrap_wrapper.hpp b/include/boost/python/detail/unwrap_wrapper.hpp new file mode 100755 index 00000000..7ae5b233 --- /dev/null +++ b/include/boost/python/detail/unwrap_wrapper.hpp @@ -0,0 +1,48 @@ +// 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) +#ifndef UNWRAP_WRAPPER_DWA2004723_HPP +# define UNWRAP_WRAPPER_DWA2004723_HPP + +# include +# include +# if defined(BOOST_PYTHON_NO_SFINAE) +# include +# include +# else +# include +# endif + +namespace boost { namespace python { namespace detail { + +# if defined(BOOST_PYTHON_NO_SFINAE) +template +struct unwrap_wrapper_helper +{ + typedef typename T::_wrapper_wrapped_type_ type; +}; + +template +typename mpl::apply_if,unwrap_wrapper_helper,mpl::identity >::type* +unwrap_wrapper(T*) +{ + return 0; +} +# else +template +typename disable_if_ret,T*>::type +unwrap_wrapper(T*) +{ + return 0; +} + +template +T* unwrap_wrapper(wrapper*) +{ + return 0; +} +# endif + +}}} // namespace boost::python::detail + +#endif // UNWRAP_WRAPPER_DWA2004723_HPP diff --git a/include/boost/python/detail/wrapper_base.hpp b/include/boost/python/detail/wrapper_base.hpp new file mode 100755 index 00000000..99d23b29 --- /dev/null +++ b/include/boost/python/detail/wrapper_base.hpp @@ -0,0 +1,86 @@ +// 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) +#ifndef WRAPPER_BASE_DWA2004722_HPP +# define WRAPPER_BASE_DWA2004722_HPP + +# include +# include +# include + +namespace boost { namespace python { + +class override; + +namespace detail +{ + class BOOST_PYTHON_DECL wrapper_base; + + namespace wrapper_base_ // ADL disabler + { + inline PyObject* get_owner(wrapper_base const volatile& w); + + inline PyObject* + owner_impl(void const volatile* x, mpl::false_) + { + return 0; + } + + template + inline PyObject* + owner_impl(T const volatile* x, mpl::true_) + { + if (wrapper_base const volatile* w = dynamic_cast(x)) + { + return wrapper_base_::get_owner(*w); + } + return 0; + } + + template + inline PyObject* + owner(T const volatile* x) + { + return wrapper_base_::owner_impl(x,is_polymorphic()); + } + } + + class BOOST_PYTHON_DECL wrapper_base + { + friend void initialize_wrapper(PyObject* self, wrapper_base* w); + friend PyObject* wrapper_base_::get_owner(wrapper_base const volatile& w); + protected: + wrapper_base() : m_self(0) {} + + override get_override( + char const* name, PyTypeObject* class_object) const; + + private: + void detach(); + + private: + PyObject* m_self; + }; + + namespace wrapper_base_ // ADL disabler + { + inline PyObject* get_owner(wrapper_base const volatile& w) + { + return w.m_self; + } + } + + inline void initialize_wrapper(PyObject* self, wrapper_base* w) + { + w->m_self = self; + } + + inline void initialize_wrapper(PyObject* self, ...) {} + + + +} // namespace detail + +}} // namespace boost::python + +#endif // WRAPPER_BASE_DWA2004722_HPP diff --git a/include/boost/python/extract.hpp b/include/boost/python/extract.hpp index ecfb6ce0..f2269b1d 100644 --- a/include/boost/python/extract.hpp +++ b/include/boost/python/extract.hpp @@ -12,16 +12,23 @@ # include # include # include -# include + # include # include -# include + # include # include # include +# include +# include namespace boost { namespace python { +namespace api +{ + class object; +} + namespace converter { template @@ -125,7 +132,7 @@ struct extract } extract(PyObject*); - extract(object const&); + extract(api::object const&); }; // @@ -138,7 +145,7 @@ inline extract::extract(PyObject* o) } template -inline extract::extract(object const& o) +inline extract::extract(api::object const& o) : base(o.ptr()) { } diff --git a/include/boost/python/object/class_metadata.hpp b/include/boost/python/object/class_metadata.hpp index d9bda9b3..ef56d560 100755 --- a/include/boost/python/object/class_metadata.hpp +++ b/include/boost/python/object/class_metadata.hpp @@ -75,7 +75,7 @@ struct register_base_of // need some registration of their own. // template -void register_shared_ptr_from_python_and_casts(T*, Bases) +inline void register_shared_ptr_from_python_and_casts(T*, Bases) { // Constructor performs registration python::detail::force_instantiate(converter::shared_ptr_from_python()); @@ -166,16 +166,13 @@ struct class_metadata use_value_holder , mpl::identity , pointee - >::type wrapper; + >::type wrapped; - // Determine whether wrapper needs to be separately registered - typedef is_base_and_derived use_callback_class; - - // Determine whether to use a holder with a back-reference + // Determine whether to use a "back-reference holder" typedef mpl::or_< - use_callback_class - , has_back_reference + has_back_reference , is_same + , is_base_and_derived > use_back_reference; // Select the holder. @@ -183,7 +180,7 @@ struct class_metadata use_back_reference , mpl::if_< use_value_holder - , value_holder_back_reference + , value_holder_back_reference , pointer_holder_back_reference > , mpl::if_< @@ -195,52 +192,75 @@ struct class_metadata inline static void register_() // Register the runtime metadata. { - objects::register_shared_ptr_from_python_and_casts((T*)0, bases()); - - class_metadata::maybe_register_callback_class(use_callback_class()); - - class_metadata::maybe_register_class_to_python(is_noncopyable()); - - class_metadata::maybe_register_pointer_to_python( - (use_value_holder*)0, (use_back_reference*)0); + class_metadata::register_aux((T*)0); } private: + template + inline static void register_aux(python::wrapper*) + { + class_metadata::register_aux2((T2*)0, mpl::true_()); + } + + inline static void register_aux(void*) + { + typedef typename is_base_and_derived::type use_callback; + class_metadata::register_aux2((T*)0, use_callback()); + } + + template + inline static void register_aux2(T2*, Callback) + { + objects::register_shared_ptr_from_python_and_casts((T2*)0, bases()); + + class_metadata::maybe_register_callback_class((T2*)0, Callback()); + + class_metadata::maybe_register_class_to_python((T2*)0, is_noncopyable()); + + class_metadata::maybe_register_pointer_to_python( + (T2*)0, (use_value_holder*)0, (use_back_reference*)0); + } + + // // Support for converting smart pointers to python // - inline static void maybe_register_pointer_to_python(void*,void*) {} - - inline static void maybe_register_pointer_to_python(mpl::false_*, mpl::false_*) + inline static void maybe_register_pointer_to_python(void*,void*,void*) {} + + template + inline static void maybe_register_pointer_to_python(T2*, mpl::false_*, mpl::false_*) { python::detail::force_instantiate( objects::class_value_wrapper< held_type - , make_ptr_instance > + , make_ptr_instance > >() ); } // // Support for registering to-python converters // - inline static void maybe_register_class_to_python(mpl::true_) {} - inline static void maybe_register_class_to_python(mpl::false_) + inline static void maybe_register_class_to_python(void*, mpl::true_) {} + + template + inline static void maybe_register_class_to_python(T2*, mpl::false_) { - python::detail::force_instantiate(class_cref_wrapper >()); + python::detail::force_instantiate(class_cref_wrapper >()); } // // Support for registering callback classes // - inline static void maybe_register_callback_class(mpl::false_) {} + inline static void maybe_register_callback_class(void*, mpl::false_) {} - inline static void maybe_register_callback_class(mpl::true_) + template + inline static void maybe_register_callback_class(T2*, mpl::true_) { objects::register_shared_ptr_from_python_and_casts( - (wrapper*)0, mpl::single_view()); + (wrapped*)0, mpl::single_view()); // explicit qualification of type_id makes msvc6 happy - objects::copy_class_object(python::type_id(), python::type_id()); + objects::copy_class_object(python::type_id(), python::type_id()); } }; diff --git a/include/boost/python/object/pointer_holder.hpp b/include/boost/python/object/pointer_holder.hpp index 764848e8..d50c689d 100644 --- a/include/boost/python/object/pointer_holder.hpp +++ b/include/boost/python/object/pointer_holder.hpp @@ -12,13 +12,17 @@ # include # include -# include # include # include + # include +# include + +# include # include # include + # include # include @@ -31,6 +35,13 @@ # include +namespace boost { namespace python { + +template class wrapper; + +}} + + namespace boost { namespace python { namespace objects { # if BOOST_WORKAROUND(__GNUC__, == 2) @@ -55,6 +66,17 @@ struct pointer_holder : instance_holder private: // required holder implementation void* holds(type_info); + + template + inline void* holds_wrapped(type_info dst_t, wrapper*,T* p) + { + return python::type_id() == dst_t ? p : 0; + } + + inline void* holds_wrapped(type_info, ...) + { + return 0; + } private: // data members Pointer m_p; @@ -107,6 +129,9 @@ void* pointer_holder::holds(type_info dst_t) if (p == 0) return 0; + if (void* wrapped = holds_wrapped(dst_t, p, p)) + return wrapped; + type_info src_t = python::type_id(); return src_t == dst_t ? p : find_dynamic_type(p, src_t, dst_t); } @@ -145,11 +170,13 @@ void* pointer_holder_back_reference::holds(type_info dst_t) # if (N != 0) template< BOOST_PP_ENUM_PARAMS_Z(1, N, class A) > # endif - pointer_holder(PyObject* BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, A, a)) + pointer_holder(PyObject* self BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, A, a)) : m_p(new Value( BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_UNFORWARD_LOCAL, nil) )) - {} + { + python::detail::initialize_wrapper(self, &*this->m_p); + } # undef N diff --git a/include/boost/python/object/value_holder.hpp b/include/boost/python/object/value_holder.hpp index f326821c..02687e33 100644 --- a/include/boost/python/object/value_holder.hpp +++ b/include/boost/python/object/value_holder.hpp @@ -13,6 +13,7 @@ # include # include +# include # include # include @@ -49,7 +50,17 @@ struct value_holder : instance_holder private: // required holder implementation void* holds(type_info); - + + template + inline void* holds_wrapped(type_info dst_t, wrapper*,T* p) + { + return python::type_id() == dst_t ? p : 0; + } + + inline void* holds_wrapped(type_info, ...) + { + return 0; + } private: // data members Value m_held; }; @@ -76,6 +87,9 @@ private: // required holder implementation template void* value_holder::holds(type_info dst_t) { + if (void* wrapped = holds_wrapped(dst_t, &m_held, &m_held)) + return wrapped; + type_info src_t = python::type_id(); return src_t == dst_t ? &m_held : find_static_type(&m_held, src_t, dst_t); @@ -114,11 +128,13 @@ void* value_holder_back_reference::holds( template # endif value_holder( - PyObject* BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, A, a)) + PyObject* self BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, A, a)) : m_held( BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_UNFORWARD_LOCAL, nil) ) - {} + { + python::detail::initialize_wrapper(self, &this->m_held); + } # undef N diff --git a/include/boost/python/override.hpp b/include/boost/python/override.hpp new file mode 100755 index 00000000..dbc00303 --- /dev/null +++ b/include/boost/python/override.hpp @@ -0,0 +1,132 @@ +#if !defined(BOOST_PP_IS_ITERATING) + +// 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) +#ifndef OVERRIDE_DWA2004721_HPP +# define OVERRIDE_DWA2004721_HPP + +# include + +# include + +# include +# include + +# include +# include +# include +# include +# include + +# include + +namespace boost { namespace python { + +class override; + +namespace detail +{ + class wrapper_base; + + // The result of calling a method. + class method_result + { + private: + friend class override; + explicit method_result(PyObject* x) + : m_obj(x) + {} + + public: + template + operator T() + { + converter::return_from_python converter; + return converter(m_obj.release()); + } + +# if !defined(BOOST_MSVC) || BOOST_WORKAROUND(_MSC_FULL_VER, > 140040607) + template + operator T&() const + { + converter::return_from_python converter; + return converter(m_obj.release()); + } +# endif + + template + T as(type* = 0) + { + converter::return_from_python converter; + return converter(m_obj.release()); + } + + template + T unchecked(type* = 0) + { + return extract(m_obj)(); + } + private: + mutable handle<> m_obj; + }; +} + +class override : public object +{ + private: + friend class detail::wrapper_base; + override(handle<> x) + : object(x) + {} + + public: + detail::method_result + operator()() const + { + detail::method_result x( + PyEval_CallFunction( + this->ptr() + , const_cast("()") + )); + return x; + } + +# define BOOST_PYTHON_fast_arg_to_python_get(z, n, _) \ + , converter::arg_to_python(a##n).get() + +# define BOOST_PP_ITERATION_PARAMS_1 (3, (1, BOOST_PYTHON_MAX_ARITY, )) +# include BOOST_PP_ITERATE() + +# undef BOOST_PYTHON_fast_arg_to_python_get +}; + +}} // namespace boost::python + +#endif // OVERRIDE_DWA2004721_HPP + +#else +# if !(BOOST_WORKAROUND(__MWERKS__, > 0x3100) \ + && BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3201))) +# line BOOST_PP_LINE(__LINE__, override.hpp) +# endif + +# define N BOOST_PP_ITERATION() + +template < + BOOST_PP_ENUM_PARAMS_Z(1, N, class A) + > +detail::method_result +operator()( BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, A, const& a) ) const +{ + detail::method_result x( + PyEval_CallFunction( + this->ptr() + , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") + BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_fast_arg_to_python_get, nil) + )); + return x; +} + +# undef N +#endif diff --git a/include/boost/python/to_python_indirect.hpp b/include/boost/python/to_python_indirect.hpp index cf457ec1..a39b723d 100644 --- a/include/boost/python/to_python_indirect.hpp +++ b/include/boost/python/to_python_indirect.hpp @@ -7,24 +7,58 @@ # include -# include # include -# include -# include -# include -# include -# include # include -# include + +# include + +# include + +# include +# include + +# include + +# if defined(__ICL) && __ICL < 600 +# include +# else +# include +# endif namespace boost { namespace python { template struct to_python_indirect { - PyObject* operator()(T ptr) const; + template + inline PyObject* + operator()(U const& ref) const + { + return this->execute(const_cast(ref), is_pointer()); + } + private: - static PyTypeObject* type(); + template + inline PyObject* execute(U* ptr, mpl::true_) const + { + // No special NULL treatment for references + if (ptr == 0) + return python::detail::none(); + else + return this->execute(*ptr, mpl::false_()); + } + + template + inline PyObject* execute(U const& x, mpl::false_) const + { + U* const p = &const_cast(x); + if (is_polymorphic::value) + { + if (PyObject* o = detail::wrapper_base_::owner(p)) + return incref(o); + } + return MakeHolder::execute(p); + } }; // @@ -34,9 +68,8 @@ namespace detail { struct make_owning_holder { - typedef PyObject* result_type; template - static result_type execute(T* p) + static PyObject* execute(T* p) { // can't use auto_ptr with Intel 5 and VC6 Dinkum library // for some reason. We get link errors against the auto_ptr @@ -48,66 +81,21 @@ namespace detail # endif typedef objects::pointer_holder holder_t; - smart_pointer ptr(p); + smart_pointer ptr(const_cast(p)); return objects::make_ptr_instance::execute(ptr); } }; struct make_reference_holder { - typedef PyObject* result_type; template - static result_type execute(T* p) + static PyObject* execute(T* p) { typedef objects::pointer_holder holder_t; - return objects::make_ptr_instance::execute(p); + T* q = const_cast(p); + return objects::make_ptr_instance::execute(q); } }; - - struct get_pointer_class - { - typedef PyTypeObject* result_type; - template - static result_type execute(T* p) - { - BOOST_STATIC_ASSERT(is_class::value); - return converter::registered::converters.class_object; - } - }; - - // null_pointer_to_none -- return none() for null pointers and 0 for all other types/values - // - // Uses simulated partial ordering - template - inline PyObject* null_pointer_to_none(T&, int) - { - return 0; - } - - // overload for pointers - template - inline PyObject* null_pointer_to_none(T* x, long) - { - return x == 0 ? python::detail::none() : 0; - } -} - -template -inline PyObject* to_python_indirect::operator()(T x) const -{ - BOOST_STATIC_ASSERT(is_pointer::value || is_reference::value); - - PyObject* const null_result = detail::null_pointer_to_none(x, 1L); - if (null_result != 0) - return null_result; - - return detail::unwind_type(x); -} - -template -inline PyTypeObject* to_python_indirect::type() -{ - return detail::unwind_type(); } }} // namespace boost::python diff --git a/include/boost/python/wrapper.hpp b/include/boost/python/wrapper.hpp new file mode 100755 index 00000000..37d24e76 --- /dev/null +++ b/include/boost/python/wrapper.hpp @@ -0,0 +1,30 @@ +// 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) +#ifndef WRAPPER_DWA2004720_HPP +# define WRAPPER_DWA2004720_HPP + +# include +# include +# include +# include + +namespace boost { namespace python { + +template +class wrapper : public detail::wrapper_base +{ +# if defined(BOOST_PYTHON_NO_SFINAE) + typedef T _wrapper_wrapped_type_; +# endif + protected: + override get_override(char const* name) const + { + return this->wrapper_base::get_override( + name, converter::registered::converters.get_class_object()); + } +}; + +}} // namespace boost::python + +#endif // WRAPPER_DWA2004720_HPP diff --git a/src/object/life_support.cpp b/src/object/life_support.cpp index 9f7645de..f4bc7b80 100644 --- a/src/object/life_support.cpp +++ b/src/object/life_support.cpp @@ -80,7 +80,7 @@ PyTypeObject life_support_type = { PyObject* make_nurse_and_patient(PyObject* nurse, PyObject* patient) { - if (nurse == Py_None) + if (nurse == Py_None || nurse == patient) return nurse; if (life_support_type.ob_type == 0) diff --git a/src/wrapper.cpp b/src/wrapper.cpp new file mode 100755 index 00000000..eac7d972 --- /dev/null +++ b/src/wrapper.cpp @@ -0,0 +1,65 @@ +// 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) + +#include + +namespace boost { namespace python { + +namespace detail +{ + override wrapper_base::get_override( + char const* name + , PyTypeObject* class_object + ) const + { + if (this->m_self) + { + if (handle<> m = handle<>( + python::allow_null( + ::PyObject_GetAttrString( + this->m_self, const_cast(name)))) + ) + { + PyObject* borrowed_f = 0; + + if ( + PyMethod_Check(m.get()) + && ((PyMethodObject*)m.get())->im_self == this->m_self + && class_object->tp_dict != 0 + ) + { + borrowed_f = ::PyDict_GetItemString(class_object->tp_dict, name); + + + } + if (borrowed_f != ((PyMethodObject*)m.get())->im_func) + return override(m); + } + } + return override(handle<>(detail::none())); + } +} + +#if 0 +namespace converter +{ + PyObject* BOOST_PYTHON_DECL do_polymorphic_ref_to_python( + python::detail::wrapper_base const volatile* x, type_info src + ) + { + if (x == 0) + { + ::PyErr_Format( + PyExc_TypeError + , "Attempting to returning pointer or reference to instance of %s\n" + "for which no corresponding Python object exists. Wrap this function" + "with a return return value policy" + ) + } + } + +} +#endif + +}} // namespace boost::python::detail diff --git a/test/Jamfile b/test/Jamfile index 1a1e663d..32dc054f 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -99,6 +99,7 @@ bpl-test crossmod_exception [ bpl-test staticmethod ] [ bpl-test shared_ptr ] [ bpl-test polymorphism ] +[ bpl-test polymorphism2 ] [ bpl-test auto_ptr ] [ bpl-test minimal ] [ bpl-test args ] diff --git a/test/embedding.cpp b/test/embedding.cpp index d280a163..d2bd021a 100644 --- a/test/embedding.cpp +++ b/test/embedding.cpp @@ -74,7 +74,7 @@ void test() )(); // Retrieve the main module's namespace - python::object main_namespace(main_module.attr("__dict__")); + python::object main_namespace((main_module.attr("__dict__"))); // Define the derived class in Python. // (You'll normally want to put this in a .py file.) @@ -115,12 +115,12 @@ test_tutorial() { using namespace boost::python; - object main_module( - handle<>(borrowed(PyImport_AddModule("__main__")))); + object main_module(( + handle<>(borrowed(PyImport_AddModule("__main__"))))); object main_namespace = main_module.attr("__dict__"); - handle<>(PyRun_String( + handle<> ignored((PyRun_String( "hello = file('hello.txt', 'w')\n" "hello.write('Hello world!')\n" @@ -129,7 +129,7 @@ test_tutorial() , Py_file_input , main_namespace.ptr() , main_namespace.ptr()) - ); + )); } void @@ -137,29 +137,29 @@ test_tutorial2() { using namespace boost::python; - object main_module( - handle<>(borrowed(PyImport_AddModule("__main__")))); + object main_module(( + handle<>(borrowed(PyImport_AddModule("__main__"))))); object main_namespace = main_module.attr("__dict__"); - handle<>(PyRun_String( + handle<> ignored((PyRun_String( "result = 5 ** 2" , Py_file_input , main_namespace.ptr() , main_namespace.ptr()) - ); + )); int five_squared = extract(main_namespace["result"]); assert(five_squared == 25); - object result(handle<>( + object result((handle<>( PyRun_String("5 ** 2" , Py_eval_input , main_namespace.ptr() , main_namespace.ptr())) - ); + )); int five_squared2 = extract(result); assert(five_squared2 == 25); diff --git a/test/polymorphism2.cpp b/test/polymorphism2.cpp new file mode 100755 index 00000000..13e8ab4d --- /dev/null +++ b/test/polymorphism2.cpp @@ -0,0 +1,159 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::python; + +struct P +{ + virtual ~P(){} + virtual char const* f() = 0; + char const* g() { return "P::g()"; } +}; + +struct PCallback : P, wrapper

+{ + char const* f() + { +#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) + return call(this->get_override("f").ptr()); +#else + return this->get_override("f")(); +#endif + } +}; + +struct Q : virtual P +{ + char const* f() { return "Q::f()"; } +}; + +struct A +{ + virtual ~A(){} + virtual char const* f() { return "A::f()"; } +}; + +struct ACallback : A, wrapper +{ + char const* f() + { + if (override f = this->get_override("f")) +#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) + return call(f.ptr()); +#else + return f(); +#endif + else + return A::f(); + } +}; + +struct B : A +{ + virtual char const* f() { return "B::f()"; } +}; + +struct C : A +{ + virtual char const* f() { return "C::f()"; } +}; + +struct D : A +{ + virtual char const* f() { return "D::f()"; } + char const* g() { return "D::g()"; } +}; + +struct DCallback : D, wrapper +{ + char const* f() + { + if (override f = this->get_override("f")) +#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) + return call(f.ptr()); +#else + return f(); +#endif + else + return D::f(); + } +}; + + +A& getBCppObj () +{ + static B b; + return b; +} + +char const* call_f(A& a) { return a.f(); } + +A* factory(unsigned choice) +{ + switch (choice % 3) + { + case 0: return new A; + break; + case 1: return new B; + break; + default: return new C; + break; + } +} + +C& getCCppObj () +{ + static C c; + return c; +} + +A* pass_a(A* x) { return x; } + +BOOST_PYTHON_MODULE_INIT(polymorphism2_ext) +{ + class_("A") + .def("f", &A::f) + ; + + def("getBCppObj", getBCppObj, return_value_policy()); + + class_,boost::noncopyable>("C") + .def("f", &C::f) + ; + + class_,boost::noncopyable>("D") + .def("f", &D::f) + .def("g", &D::g) + ; + + def("pass_a", &pass_a, return_internal_reference<>()); + + def("getCCppObj", getCCppObj, return_value_policy()); + + def("factory", factory, return_value_policy()); + + def("call_f", call_f); + + class_("P") + .def("f", pure_virtual(&P::f)) + ; + + class_ >("Q") + .def("g", &P::g) // make sure virtual inheritance doesn't interfere + ; +} + +//#include "module_tail.cpp" diff --git a/test/polymorphism2.py b/test/polymorphism2.py new file mode 100644 index 00000000..79d87b37 --- /dev/null +++ b/test/polymorphism2.py @@ -0,0 +1,71 @@ +import unittest +from polymorphism2_ext import * + +class PolymorphTest(unittest.TestCase): + + def testReturnCpp(self): + + # Python Created Object With Same Id As + # Cpp Created B Object + # b = B(872) + + # Get Reference To Cpp Created B Object + a = getBCppObj() + + # Python Created B Object and Cpp B Object + # Should have same result by calling f() + self.failUnlessEqual ('B::f()', a.f()) + self.failUnlessEqual ('B::f()', call_f(a)) + self.failUnlessEqual ('A::f()', call_f(A())) + + def test_references(self): + # B is not exposed to Python + a = getBCppObj() + self.failUnlessEqual(type(a), A) + + # C is exposed to Python + c = getCCppObj() + self.failUnlessEqual(type(c), C) + + def test_factory(self): + self.failUnlessEqual(type(factory(0)), A) + self.failUnlessEqual(type(factory(1)), A) + self.failUnlessEqual(type(factory(2)), C) + + def test_return_py(self): + + class X(A): + def f(self): + return 'X.f' + + x = X() + + self.failUnlessEqual ('X.f', x.f()) + self.failUnlessEqual ('X.f', call_f(x)) + + def test_wrapper_downcast(self): + a = pass_a(D()) + self.failUnlessEqual('D::g()', a.g()) + + def test_pure_virtual(self): + p = P() + self.assertRaises(RuntimeError, p.f) + + q = Q() + self.failUnlessEqual ('Q::f()', q.f()) + + class R(P): + def f(self): + return 'R.f' + + r = R() + self.failUnlessEqual ('R.f', r.f()) + + +if __name__ == "__main__": + + # remove the option which upsets unittest + import sys + sys.argv = [ x for x in sys.argv if x != '--broken-auto-ptr' ] + + unittest.main()