From 0c8aa84f2ffff670ca9bcc17d614982e06d09db2 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Fri, 20 Dec 2002 18:19:18 +0000 Subject: [PATCH] Enable automatic downcasting to registered classes for pointers, references, and smart pointers [SVN r16673] --- include/boost/python/detail/decref_guard.hpp | 22 +++++++ include/boost/python/object/make_instance.hpp | 51 +++++++--------- .../boost/python/object/make_ptr_instance.hpp | 60 +++++++++++++++++++ include/boost/python/object/select_holder.hpp | 4 +- include/boost/python/to_python_indirect.hpp | 6 +- test/polymorphism.cpp | 41 ++++++++++++- test/polymorphism.py | 24 ++++++-- test/shared_ptr.cpp | 15 +++++ test/shared_ptr.py | 6 ++ 9 files changed, 188 insertions(+), 41 deletions(-) create mode 100644 include/boost/python/detail/decref_guard.hpp create mode 100644 include/boost/python/object/make_ptr_instance.hpp diff --git a/include/boost/python/detail/decref_guard.hpp b/include/boost/python/detail/decref_guard.hpp new file mode 100644 index 00000000..67455706 --- /dev/null +++ b/include/boost/python/detail/decref_guard.hpp @@ -0,0 +1,22 @@ +// 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. +#ifndef DECREF_GUARD_DWA20021220_HPP +# define DECREF_GUARD_DWA20021220_HPP + +namespace boost { namespace python { namespace detail { + +struct decref_guard +{ + decref_guard(PyObject* o) : obj(o) {} + ~decref_guard() { Py_XDECREF(obj); } + void cancel() { obj = 0; } + private: + PyObject* obj; +}; + +}}} // namespace boost::python::detail + +#endif // DECREF_GUARD_DWA20021220_HPP diff --git a/include/boost/python/object/make_instance.hpp b/include/boost/python/object/make_instance.hpp index b9e0c981..eb420d2f 100644 --- a/include/boost/python/object/make_instance.hpp +++ b/include/boost/python/object/make_instance.hpp @@ -8,42 +8,34 @@ # include # include +# include namespace boost { namespace python { namespace objects { -struct decref_guard -{ - decref_guard(PyObject* o) : obj(o) {} - ~decref_guard() { Py_XDECREF(obj); } - void cancel() { obj = 0; } - private: - PyObject* obj; -}; - -template -struct make_instance +template +struct make_instance_impl { typedef objects::instance instance_t; template - static PyObject* execute(Arg& x) + static inline PyObject* execute(Arg& x) { BOOST_STATIC_ASSERT(is_class::value); - - PyTypeObject* type = converter::registered::converters.get_class_object(); + + PyTypeObject* type = Derived::get_class_object(x); PyObject* raw_result = type->tp_alloc( type, objects::additional_instance_size::value); if (raw_result != 0) { - decref_guard protect(raw_result); + python::detail::decref_guard protect(raw_result); instance_t* instance = (instance_t*)raw_result; // construct the new C++ object and install the pointer // in the Python object. - construct(instance, x)->install(raw_result); + Derived::construct(&instance->storage, (PyObject*)instance, x)->install(raw_result); // Note the position of the internally-stored Holder, // for the sake of destruction @@ -54,23 +46,22 @@ struct make_instance } return raw_result; } +}; - private: - // Kind of a hack to support code re-use. The first form is used - // to construct holders around pointers or smart pointers. The - // second form is used to construct holders around by-value - // returns. We have to pass a pointer to the owning Python object - // to the second form in order to make it forward the 2nd argument - // on to the constructor of its embedded T object. - template - static Holder* construct(instance_t* result, Arg& x) - { - return new ((void*)&result->storage) Holder(x); - } - static Holder* construct(instance_t* result, reference_wrapper x) +template +struct make_instance + : make_instance_impl > +{ + template + static inline PyTypeObject* get_class_object(U&) { - return new ((void*)&result->storage) Holder((PyObject*)result, x); + return converter::registered::converters.get_class_object(); + } + + static inline Holder* construct(void* storage, PyObject* instance, reference_wrapper x) + { + return new (storage) Holder(instance, x); } }; diff --git a/include/boost/python/object/make_ptr_instance.hpp b/include/boost/python/object/make_ptr_instance.hpp new file mode 100644 index 00000000..40debeaa --- /dev/null +++ b/include/boost/python/object/make_ptr_instance.hpp @@ -0,0 +1,60 @@ +// 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. +#ifndef MAKE_PTR_INSTANCE_DWA200296_HPP +# define MAKE_PTR_INSTANCE_DWA200296_HPP + +# include +# include +# include +# include +# include + +namespace boost { namespace python { namespace objects { + +template +struct make_ptr_instance + : make_instance_impl > +{ + template + static inline Holder* construct(void* storage, PyObject*, Arg& x) + { + return new (storage) Holder(x); + } + + template + static inline PyTypeObject* get_class_object(Ptr const& x) + { + return get_class_object_impl(get_pointer(x)); + } + + private: + template + static inline PyTypeObject* get_class_object_impl(U const volatile* p) + { + PyTypeObject* derived = get_derived_class_object(is_polymorphic::type(), p); + if (derived) + return derived; + return converter::registered::converters.get_class_object(); + } + + template + static inline PyTypeObject* get_derived_class_object(mpl::true_c, U const volatile* x) + { + converter::registration const* r = converter::registry::query(type_info(typeid(*x))); + return r ? r->m_class_object : 0; + } + + template + static inline PyTypeObject* get_derived_class_object(mpl::false_c, U*) + { + return 0; + } +}; + + +}}} // namespace boost::python::object + +#endif // MAKE_PTR_INSTANCE_DWA200296_HPP diff --git a/include/boost/python/object/select_holder.hpp b/include/boost/python/object/select_holder.hpp index 46f3e212..2c032550 100644 --- a/include/boost/python/object/select_holder.hpp +++ b/include/boost/python/object/select_holder.hpp @@ -12,7 +12,7 @@ # include # include # include -# include +# include # include # include # include @@ -142,7 +142,7 @@ namespace detail static inline void register_(mpl::false_c) { python::detail::force_instantiate( - objects::class_value_wrapper >()); + objects::class_value_wrapper >()); } }; } diff --git a/include/boost/python/to_python_indirect.hpp b/include/boost/python/to_python_indirect.hpp index c1ad8ce5..215cb198 100644 --- a/include/boost/python/to_python_indirect.hpp +++ b/include/boost/python/to_python_indirect.hpp @@ -13,7 +13,7 @@ # include # include # include -# include +# include # include namespace boost { namespace python { @@ -48,7 +48,7 @@ namespace detail typedef objects::pointer_holder holder_t; smart_pointer ptr(p); - return objects::make_instance::execute(ptr); + return objects::make_ptr_instance::execute(ptr); } }; @@ -59,7 +59,7 @@ namespace detail static result_type execute(T* p) { typedef objects::pointer_holder holder_t; - return objects::make_instance::execute(p); + return objects::make_ptr_instance::execute(p); } }; diff --git a/test/polymorphism.cpp b/test/polymorphism.cpp index f648448c..02ec7b57 100644 --- a/test/polymorphism.cpp +++ b/test/polymorphism.cpp @@ -3,7 +3,14 @@ // 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 using namespace boost::python; @@ -40,6 +47,11 @@ struct B : A virtual std::string f() { return "B::f()"; } }; +struct C : A +{ + virtual std::string f() { return "C::f()"; } +}; + A& getBCppObj () { static B b; @@ -48,6 +60,25 @@ A& getBCppObj () std::string 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; +} + BOOST_PYTHON_MODULE_INIT(polymorphism_ext) { class_("A") @@ -56,6 +87,14 @@ BOOST_PYTHON_MODULE_INIT(polymorphism_ext) def("getBCppObj", getBCppObj, return_value_policy()); + class_,boost::noncopyable>("C") + .def("f", &C::f) + ; + + def("getCCppObj", getCCppObj, return_value_policy()); + + def("factory", factory, return_value_policy()); + def("call_f", call_f); } diff --git a/test/polymorphism.py b/test/polymorphism.py index b1836d79..a305bbea 100644 --- a/test/polymorphism.py +++ b/test/polymorphism.py @@ -17,17 +17,31 @@ class PolymorphTest(unittest.TestCase): 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 testReturnPy(self): - class C(A): + class D(A): def f(self): - return 'C.f' + return 'D.f' - c = C() + d = D() - self.failUnlessEqual ('C.f', c.f()) - self.failUnlessEqual ('C.f', call_f(c)) + self.failUnlessEqual ('D.f', d.f()) + self.failUnlessEqual ('D.f', call_f(d)) if __name__ == "__main__": diff --git a/test/shared_ptr.cpp b/test/shared_ptr.cpp index c249547f..5f1b36ce 100644 --- a/test/shared_ptr.cpp +++ b/test/shared_ptr.cpp @@ -82,6 +82,16 @@ struct ZWrap : Z PyObject* m_self; }; +struct YY : Y +{ + YY(int n) : Y(n) {} +}; + +shared_ptr factory(int n) +{ + return shared_ptr(n < 42 ? new Y(n) : new YY(n)); +} + static int stored_v() { return functions::get()->v(); } BOOST_PYTHON_MODULE(shared_ptr_ext) @@ -90,6 +100,8 @@ BOOST_PYTHON_MODULE(shared_ptr_ext) .def("value", &X::value) ; + def("factory", factory); + functions::expose(); def("x_count", &X::count); def("x_release", &functions::release_store); @@ -99,6 +111,9 @@ BOOST_PYTHON_MODULE(shared_ptr_ext) .def("value", &Y::value) ; + class_, boost::noncopyable>("YY", init()) + ; + functions::expose(); def("y_count", &Y::count); def("y_release", &functions::release_store); diff --git a/test/shared_ptr.py b/test/shared_ptr.py index 62e0b4c5..64850097 100644 --- a/test/shared_ptr.py +++ b/test/shared_ptr.py @@ -1,5 +1,11 @@ ''' >>> from shared_ptr_ext import * + +>>> type(factory(3)) + +>>> type(factory(42)) + + >>> class P(Z): ... def v(self): ... return -Z.v(self);