diff --git a/include/boost/python/class.hpp b/include/boost/python/class.hpp index 9676cd32..3025c58f 100644 --- a/include/boost/python/class.hpp +++ b/include/boost/python/class.hpp @@ -8,37 +8,43 @@ # include # include + # include - # include - # include -# include -# include -# include -# include -# include -# include -# include -# include -# include # include -# include -# include # include -# include # include # include # include -# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# include +# include +# include +# include +# include +# include + +# include # include # include # include # include # include +# include + namespace boost { namespace python { enum no_init_t { no_init }; @@ -89,6 +95,54 @@ namespace detail { SelectHolder::register_(); } + + namespace error + { + // + // A meta-assertion mechanism which prints nice error messages and + // backtraces on lots of compilers. Usage: + // + // assertion::failed + // + // where C is an MPL metafunction class + // + + template struct assertion_failed { }; + template struct assertion_ok { typedef C failed; }; + + template + struct assertion + : mpl::if_, assertion_failed >::type + {}; + + // + // Checks for validity of arguments used to define virtual + // functions with default implementations. + // + + template + void not_a_derived_class_member(Default) {} + + template + struct virtual_function_default +# if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 + : assertion >::failed + , assertion >::failed +# endif + { + template + static void + must_be_derived_class_member(Default const&) + { + typedef typename assertion > >::failed test0; +# if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 + typedef typename assertion >::failed test1; + typedef typename assertion >::failed test2; +# endif + not_a_derived_class_member(Fn()); + } + }; + } } // This is the primary mechanism through which users will expose @@ -313,22 +367,6 @@ class class_ : public objects::class_base // things which are already wrapped into callable python::object // instances and everything else. // - template - inline void def_impl( - char const* name - , Fn fn - , Helper const& helper - , ...) - { - objects::add_to_namespace( - *this, name, - make_function( - // This bit of nastiness casts F to a member function of T if possible. - detail::member_function_cast::stage1(fn).stage2((T*)0).stage3(fn) - , helper.policies(), helper.keywords()) - , helper.doc()); - } - template inline void def_impl( char const* name @@ -345,6 +383,50 @@ class class_ : public objects::class_base objects::add_to_namespace(*this, name, f, helper.doc()); } + template + inline void def_impl( + char const* name + , Fn fn + , Helper const& helper + , ...) + { + objects::add_to_namespace( + *this, name, + make_function( + // This bit of nastiness casts F to a member function of T if possible. + detail::member_function_cast::stage1(fn).stage2((T*)0).stage3(fn) + , helper.policies(), helper.keywords()) + , helper.doc()); + + this->def_default(name, fn, helper, mpl::bool_c()); + } + + // + // These two overloads handle the definition of default + // implementation overloads for virtual functions. The second one + // handles the case where no default implementation was specified. + // + template + inline void def_default( + char const* name + , Fn fn + , Helper const& helper + , mpl::bool_c) + { + detail::error::virtual_function_default::must_be_derived_class_member( + helper.default_implementation()); + + objects::add_to_namespace( + *this, name, + make_function( + helper.default_implementation(), helper.policies(), helper.keywords()) + ); + } + + template + inline void def_default(char const*, Fn, Helper const&, mpl::bool_c) + { } + // // These two overloads discriminate between def() as applied to // regular functions and def() as applied to the result of diff --git a/include/boost/python/detail/caller.hpp b/include/boost/python/detail/caller.hpp index 808f95f5..3477f64d 100644 --- a/include/boost/python/detail/caller.hpp +++ b/include/boost/python/detail/caller.hpp @@ -56,7 +56,7 @@ struct select_result_converter }; -template struct caller_gen; +template struct caller_arity; # define BOOST_PYTHON_NEXT(init,name,n) \ typedef BOOST_PP_IF(n,typename BOOST_PP_CAT(name,BOOST_PP_DEC(n)) ::next, init) name##n; @@ -81,8 +81,8 @@ template struct caller_gen; template struct caller_base_select { - enum { n_arguments = mpl::size::value - 1 }; - typedef typename caller_gen::template impl type; + enum { arity = mpl::size::value - 1 }; + typedef typename caller_arity::template impl type; }; // A function object type which wraps C++ objects as Python callable @@ -131,7 +131,7 @@ struct caller # define N BOOST_PP_ITERATION() template <> -struct caller_gen +struct caller_arity { template struct impl diff --git a/include/boost/python/detail/def_helper.hpp b/include/boost/python/detail/def_helper.hpp index fa66f04a..d78ddb81 100644 --- a/include/boost/python/detail/def_helper.hpp +++ b/include/boost/python/detail/def_helper.hpp @@ -97,12 +97,11 @@ namespace detail template struct doc_extract : tuple_extract< - Tuple, - mpl::logical_not< + Tuple + , mpl::logical_not< mpl::logical_or< is_reference_to_class - , is_reference_to_function_pointer - , is_reference_to_function + , is_reference_to_member_function_pointer > > > @@ -118,8 +117,8 @@ namespace detail template struct policy_extract : tuple_extract< - Tuple, - mpl::logical_and< + Tuple + , mpl::logical_and< mpl::logical_not > , is_reference_to_class , mpl::logical_not > @@ -131,12 +130,9 @@ namespace detail template struct default_implementation_extract : tuple_extract< - Tuple, - mpl::logical_or< - is_reference_to_function_pointer - , is_reference_to_function + Tuple + , is_reference_to_member_function_pointer > - > { }; @@ -161,8 +157,8 @@ namespace detail , default_call_policies , keywords<0> , char const* - , void(*)() // A function pointer type which is never an - // appropriate default implementation + , void(not_specified::*)() // A function pointer type which is never an + // appropriate default implementation > all_t; // Constructors; these initialize an member of the tuple type @@ -172,15 +168,16 @@ namespace detail def_helper(T1 const& a1, T2 const& a2, T3 const& a3) : m_all(a1,a2,a3,m_nil) {} def_helper(T1 const& a1, T2 const& a2, T3 const& a3, T4 const& a4) : m_all(a1,a2,a3,a4) {} - private: // type + private: // types typedef typename default_implementation_extract::result_type default_implementation_t; + + public: // Constants which can be used for static assertions. - public: // Constant which can be used for static assertions. Users - // must not supply a default implementation for non-class - // methods. + // Users must not supply a default implementation for non-class + // methods. BOOST_STATIC_CONSTANT( bool, has_default_implementation = ( - !is_same::value)); + !is_same::value)); public: // Extractor functions which pull the appropriate value out // of the tuple @@ -201,7 +198,7 @@ namespace detail default_implementation_t default_implementation() const { - return policy_extract::extract(m_all); + return default_implementation_extract::extract(m_all); } private: // data members diff --git a/include/boost/python/detail/indirect_traits.hpp b/include/boost/python/detail/indirect_traits.hpp index 31481d76..d53e396c 100644 --- a/include/boost/python/detail/indirect_traits.hpp +++ b/include/boost/python/detail/indirect_traits.hpp @@ -302,8 +302,7 @@ typename is_const_help::type reference_to_const_helper(V&); outer_no_type reference_to_const_helper(...); -template -struct is_reference_to_const_helper1 +struct true_helper1 { template struct apply @@ -315,6 +314,21 @@ struct is_reference_to_const_helper1 }; }; +template +struct is_reference_to_const_helper1 : true_helper1 +{ +# if 0 + template + struct apply + { + static T t; + BOOST_STATIC_CONSTANT( + bool, value + = sizeof(reference_to_const_helper(t)) == sizeof(inner_yes_type)); + }; +# endif +}; + template <> struct is_reference_to_const_helper1 : false_helper1 { diff --git a/include/boost/python/detail/invoke.hpp b/include/boost/python/detail/invoke.hpp index 4f59f44c..b83c6e34 100644 --- a/include/boost/python/detail/invoke.hpp +++ b/include/boost/python/detail/invoke.hpp @@ -11,7 +11,6 @@ # include # include # include -# include # include @@ -52,7 +51,6 @@ template struct is_defaulted_virtual_fn; // Tag types describing invocation methods struct fn_tag {}; struct mem_fn_tag {}; -struct virtual_fn_tag {}; // A metafunction returning the appropriate tag type for invoking an // object of type T. @@ -61,11 +59,7 @@ struct invoke_tag : mpl::if_< is_member_function_pointer , mem_fn_tag - , typename mpl::if_< - is_defaulted_virtual_fn - , virtual_fn_tag - , fn_tag - >::type + , fn_tag > {}; @@ -106,25 +100,6 @@ inline PyObject* invoke(mem_fn_tag, void_result_to_python, F& f, TC& tc BOOST_PP return none(); } -template -inline PyObject* invoke(virtual_fn_tag, RC*, F& f, TC& tc BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, AC, & ac) ) -{ - return RC()( - tc.use_default() - ? f.default_impl(tc() BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, ac, () BOOST_PP_INTERCEPT)) - : (tc().*f.dispatch)(BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, ac, () BOOST_PP_INTERCEPT)) ); -} - -template -inline PyObject* invoke(virtual_fn_tag, void_result_to_python, F& f, TC& tc BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, AC, & ac) ) -{ - if (tc.use_default()) - f.default_impl(tc() BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, ac,()BOOST_PP_INTERCEPT)); - else - (tc().*f.dispatch)(BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, ac,()BOOST_PP_INTERCEPT)); - return none(); -} - # undef N #endif // BOOST_PP_IS_ITERATING diff --git a/include/boost/python/detail/this_arg_from_python.hpp b/include/boost/python/detail/this_arg_from_python.hpp index aa8c6c7b..36ad7ae7 100644 --- a/include/boost/python/detail/this_arg_from_python.hpp +++ b/include/boost/python/detail/this_arg_from_python.hpp @@ -1,3 +1,4 @@ +#error obsolete // 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 @@ -7,6 +8,7 @@ # define THIS_ARG_FROM_PYTHON_DWA20021122_HPP # include +# include # include @@ -14,75 +16,79 @@ namespace boost { namespace python { namespace detail { - template - struct this_ptr_arg_from_python : converter::arg_lvalue_from_python_base - { - this_ptr_arg_from_python(PyObject* x) - : converter::arg_lvalue_from_python_base( - converter::get_lvalue_from_python(x, converter::registered::converters)) - {} +template +struct this_ptr_arg_from_python : converter::arg_lvalue_from_python_base +{ + this_ptr_arg_from_python(PyObject* x) + : converter::arg_lvalue_from_python_base( + converter::get_lvalue_from_python(x, converter::registered::converters)) + {} - typedef T* result_type; - T* operator()() const - { - return static_cast(this->result()); - } - - bool use_default() const - { - return dynamic_cast((*this)()); - } - }; - - template - struct this_ref_arg_from_python : this_ptr_arg_from_python - { - typedef this_ptr_arg_from_python base; - this_ref_arg_from_python(PyObject* x) : base(x) {} - typedef T& result_type; - - result_type operator()() const - { - return *this->base::operator()(); - } - }; + typedef T* result_type; + T* operator()() const + { + return static_cast(this->result()); + } - // An MPL metafunction class which returns a `this' converter - // appropriate for ArgType, where the target of the member function is - // a class of type T. - template - struct gen_this_from_python - { - // Note: there will almost always be an compile-time error if the - // argument type is neither a reference nor a pointer, since T* - // will be extracted in that case and passed on to the wrapped - // function. - template struct apply - { - BOOST_STATIC_CONSTANT( - bool, use_ptr - = is_pointer::value - || boost::python::detail::is_reference_to_pointer::value - && boost::python::detail::is_reference_to_const::value - && !boost::python::detail::is_reference_to_volatile::value); - - typedef typename mpl::if_c< - use_ptr - , this_ptr_arg_from_python - , this_ref_arg_from_python - >::type type; - }; - }; + bool use_default() const + { + return dynamic_cast((*this)()); + } +}; - // An MPL iterator which iterates over a sequence whose first element - // is gen_this_from_python and the remainder of which is an endless - // sequence of gen_arg_from_python - template - struct method_args_from_python - { - typedef gen_this_from_python type; - typedef args_from_python next; - }; +template +struct this_ref_arg_from_python : this_ptr_arg_from_python +{ + typedef this_ptr_arg_from_python base; + this_ref_arg_from_python(PyObject* x) : base(x) {} + typedef T& result_type; + + result_type operator()() const + { + return *this->base::operator()(); + } +}; + +// An MPL metafunction class which returns a `this' converter +// appropriate for ArgType, where the target of the member function is +// a class of type T. +template +struct gen_this_from_python +{ + // Note: there will almost always be an compile-time error if the + // argument type is neither a reference nor a pointer, since T* + // will be extracted in that case and passed on to the wrapped + // function. + template struct apply + { + BOOST_STATIC_CONSTANT( + bool, use_ptr + = is_pointer::value + || boost::python::detail::is_reference_to_pointer::value + && boost::python::detail::is_reference_to_const::value + && !boost::python::detail::is_reference_to_volatile::value); + + typedef typename mpl::if_c< + is_back_reference::value + , nullary > + , typename mpl::if_c< + use_ptr + , this_ptr_arg_from_python + , this_ref_arg_from_python + >::type + >::type type; + }; +}; + +// An MPL iterator which iterates over a sequence whose first element +// is gen_this_from_python and the remainder of which is an endless +// sequence of gen_arg_from_python +template +struct method_args_from_python +{ + typedef gen_this_from_python type; + typedef args_from_python next; +}; template struct defaulted_virtual_fn @@ -94,8 +100,6 @@ struct defaulted_virtual_fn Default default_impl; }; - - # ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template struct is_defaulted_virtual_fn diff --git a/include/boost/python/object/function_handle.hpp b/include/boost/python/object/function_handle.hpp index 96cf82a5..29efa2be 100644 --- a/include/boost/python/object/function_handle.hpp +++ b/include/boost/python/object/function_handle.hpp @@ -10,6 +10,7 @@ # include # include # include +# include namespace boost { namespace python { namespace objects { diff --git a/test/Jamfile b/test/Jamfile index 65c3bb76..d6e8175e 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -61,6 +61,7 @@ run ../test/embedding.cpp ../build/boost_python : # requirements $(PYTHON_PROPERTIES) ; +bpl-test polymorphism ; bpl-test auto_ptr ; bpl-test minimal ; bpl-test args ; diff --git a/test/polymorphism.cpp b/test/polymorphism.cpp new file mode 100644 index 00000000..f648448c --- /dev/null +++ b/test/polymorphism.cpp @@ -0,0 +1,62 @@ +// 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 + +using namespace boost::python; + +struct Callback +{ + Callback(PyObject* o) : mSelf(o) {} + PyObject* mSelf; +}; + +struct A +{ + virtual ~A(){} + virtual std::string f() { return "A::f()"; } +}; + +struct ACallback : A, Callback +{ + ACallback (PyObject* self) : Callback(self) {} + + + std::string f() + { + return call_method(mSelf, "f"); + } + + std::string default_f() + { + return A::f(); + } +}; + +struct B : A +{ + virtual std::string f() { return "B::f()"; } +}; + +A& getBCppObj () +{ + static B b; + return b; +} + +std::string call_f(A& a) { return a.f(); } + +BOOST_PYTHON_MODULE_INIT(polymorphism_ext) +{ + class_("A") + .def("f", &A::f, &ACallback::default_f) + ; + + def("getBCppObj", getBCppObj, return_value_policy()); + + def("call_f", call_f); +} + +//#include "module_tail.cpp" diff --git a/test/polymorphism.py b/test/polymorphism.py new file mode 100644 index 00000000..b1836d79 --- /dev/null +++ b/test/polymorphism.py @@ -0,0 +1,38 @@ +import unittest +from polymorphism_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 testReturnPy(self): + + class C(A): + def f(self): + return 'C.f' + + c = C() + + self.failUnlessEqual ('C.f', c.f()) + self.failUnlessEqual ('C.f', call_f(c)) + +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()