From e14e4e156ce79fd62003977874fbd7ea00ccee9f Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sun, 24 Nov 2002 02:43:24 +0000 Subject: [PATCH] New function invocation mechanism. This is the major groundwork for handling virtual functions with default implementations properly [SVN r16388] --- include/boost/python/class.hpp | 8 +- .../boost/python/converter/registrations.hpp | 15 +- .../boost/python/detail/arg_tuple_size.hpp | 1 + include/boost/python/detail/caller.hpp | 229 ++++++++++++------ include/boost/python/detail/invoke.hpp | 130 ++++++++++ .../python/detail/make_keyword_range_fn.hpp | 46 ++-- .../python/detail/this_arg_from_python.hpp | 122 ++++++++++ include/boost/python/make_function.hpp | 130 ++++++---- .../boost/python/object/function_handle.hpp | 19 +- src/converter/arg_to_python_base.cpp | 34 ++- src/converter/registry.cpp | 2 +- test/m1.cpp | 5 + test/newtest.py | 11 + 13 files changed, 563 insertions(+), 189 deletions(-) create mode 100644 include/boost/python/detail/invoke.hpp create mode 100644 include/boost/python/detail/this_arg_from_python.hpp diff --git a/include/boost/python/class.hpp b/include/boost/python/class.hpp index 26ca5bfd..10d11c1b 100644 --- a/include/boost/python/class.hpp +++ b/include/boost/python/class.hpp @@ -107,12 +107,8 @@ namespace detail }; } -// -// class_ -// -// This is the primary mechanism through which users will expose -// C++ classes to Python. The three template arguments are: -// +// This is the primary mechanism through which users will expose +// C++ classes to Python. template < class T // class being wrapped , class X1 // = detail::not_specified diff --git a/include/boost/python/converter/registrations.hpp b/include/boost/python/converter/registrations.hpp index af2dd510..8bbe1621 100644 --- a/include/boost/python/converter/registrations.hpp +++ b/include/boost/python/converter/registrations.hpp @@ -27,10 +27,15 @@ struct rvalue_from_python_chain rvalue_from_python_chain* next; }; -struct registration +struct BOOST_PYTHON_DECL registration { + public: // member functions explicit registration(type_info); + // Convert the appropriately-typed data to Python + PyObject* to_python(void const volatile*) const; + + public: // data members. So sue me. const python::type_info target_type; // The chain of eligible from_python converters when an lvalue is required @@ -39,11 +44,11 @@ struct registration // The chain of eligible from_python converters when an rvalue is acceptable rvalue_from_python_chain* rvalue_chain; - // The unique to_python converter for the associated C++ type. - to_python_function_t to_python; - // The class object associated with this type PyTypeObject* class_object; + + // The unique to_python converter for the associated C++ type. + to_python_function_t m_to_python; }; // @@ -53,7 +58,7 @@ inline registration::registration(type_info target_type) : target_type(target_type) , lvalue_chain(0) , rvalue_chain(0) - , to_python(0) + , m_to_python(0) , class_object(0) {} diff --git a/include/boost/python/detail/arg_tuple_size.hpp b/include/boost/python/detail/arg_tuple_size.hpp index 88900702..cad72c39 100644 --- a/include/boost/python/detail/arg_tuple_size.hpp +++ b/include/boost/python/detail/arg_tuple_size.hpp @@ -1,3 +1,4 @@ +#error obsolete #if !defined(BOOST_PP_IS_ITERATING) // (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and diff --git a/include/boost/python/detail/caller.hpp b/include/boost/python/detail/caller.hpp index 9d390dbd..808f95f5 100644 --- a/include/boost/python/detail/caller.hpp +++ b/include/boost/python/detail/caller.hpp @@ -1,106 +1,175 @@ #if !defined(BOOST_PP_IS_ITERATING) -// (C) Copyright David Abrahams 2000. 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. -// -// The author gratefully acknowleges the support of Dragon Systems, Inc., in -// producing this work. -// -// This file generated for 10-argument member functions and 11-argument free -// functions by gen_caller.python +// 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 CALLER_DWA20011214_HPP -# define CALLER_DWA20011214_HPP +# ifndef CALLER_DWA20021121_HPP +# define CALLER_DWA20021121_HPP + +# include + +# include +# include +# include +# include -# include -# include # include - -# include -# include - -# include # include -# include +# include # include +# include +# include +# include +# include -namespace boost { namespace python +# include + +namespace boost { namespace python { namespace detail { + +// This "result converter" is really just used as +// a dispatch tag to invoke(...), selecting the appropriate +// implementation +typedef int void_result_to_python; + +// A metafunction taking an iterator FunctionIter to a metafunction +// class and an iterator ArgIter to an argument, which applies the +// result of dereferencing FunctionIter to the result of dereferencing +// ArgIter +template +struct apply_iter1 + : mpl::apply1 {}; + +// Given a model of CallPolicies and a C++ result type, this +// metafunction selects the appropriate converter to use for +// converting the result to python. +template +struct select_result_converter + : mpl::if_< + is_same + , void_result_to_python + , typename mpl::apply1::type* + > { - template struct to_python; -}} - -namespace boost { namespace python { namespace detail { - -struct caller -{ - typedef PyObject* result_type; - - // function pointers -# define BOOST_PP_ITERATION_PARAMS_1 \ - (4, (0, BOOST_PYTHON_MAX_ARITY, , BOOST_PYTHON_FUNCTION_POINTER)) -# include BOOST_PP_ITERATE() - - // pointers-to-members -# define BOOST_PP_ITERATION_PARAMS_1 \ - (4, (0, 3, , BOOST_PYTHON_POINTER_TO_MEMBER)) -# include BOOST_PP_ITERATE() - }; + +template struct caller_gen; + +# 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; + +# define BOOST_PYTHON_ARG_CONVERTER(n) \ + BOOST_PYTHON_NEXT(typename first::next, arg_iter,n) \ + BOOST_PYTHON_NEXT(ConverterGenerators, conv_iter,n) \ + typedef typename apply_iter1::type c_t##n; \ + c_t##n c##n(PyTuple_GET_ITEM(args_, n)); \ + if (!c##n.convertible()) \ + return 0; + +# define BOOST_PP_ITERATION_PARAMS_1 \ + (3, (0, BOOST_PYTHON_MAX_ARITY + 1, )) +# include BOOST_PP_ITERATE() + +# undef BOOST_PYTHON_ARG_CONVERTER +# undef BOOST_PYTHON_NEXT + +// A metafunction returning the base class used for caller. +template +struct caller_base_select +{ + enum { n_arguments = mpl::size::value - 1 }; + typedef typename caller_gen::template impl type; +}; + +// A function object type which wraps C++ objects as Python callable +// objects. +// +// Template Arguments: +// +// F - +// the C++ `function object' that will be called. Might +// actually be any data for which an appropriate invoke_tag() can +// be generated. invoke(...) takes care of the actual invocation syntax. +// +// ConverterGenerators - +// An MPL iterator type over a sequence of metafunction classes +// that can be applied to element 1...N of Sig to produce +// argument from_python converters for the arguments +// +// CallPolicies - +// The precall, postcall, and what kind of resultconverter to +// generate for mpl::front::type +// +// Sig - +// The `intended signature' of the function. An MPL sequence +// beginning with a result type and continuing with a list of +// argument types. +template +struct caller + : caller_base_select::type +{ + typedef typename caller_base_select< + F,ConverterGenerators,CallPolicies,Sig + >::type base; + + typedef PyObject* result_type; + + caller(F f, CallPolicies p) : base(f,p) {} +}; + + }}} // namespace boost::python::detail -# endif // CALLER_DWA20011214_HPP +# endif // CALLER_DWA20021121_HPP -/* ---------- function pointers --------- */ -#elif BOOST_PP_ITERATION_DEPTH() == 1 && BOOST_PP_ITERATION_FLAGS() == BOOST_PYTHON_FUNCTION_POINTER -# line BOOST_PP_LINE(__LINE__, detail/caller.hpp(function pointers)) +#else # define N BOOST_PP_ITERATION() -template < - class P, class R - BOOST_PP_ENUM_TRAILING_PARAMS_Z(1, N, class A) - > -PyObject* operator()( - R (*pf)(BOOST_PP_ENUM_PARAMS_Z(1, N, A)) - , PyObject* args - , PyObject* keywords - , P const& policies) const +template <> +struct caller_gen { - return returning::call(pf, args, keywords, &policies); -} + template + struct impl + { + impl(F f, Policies p) : m_data(f,p) {} -# undef N + PyObject* operator()(PyObject* args_, PyObject*) // eliminate + // this + // trailing + // keyword dict + { + typedef typename mpl::begin::type first; + typedef typename first::type result_t; + typedef typename select_result_converter::type result_converter; +# if N +# define BOOST_PP_LOCAL_MACRO(i) BOOST_PYTHON_ARG_CONVERTER(i) +# define BOOST_PP_LOCAL_LIMITS (0, N-1) +# include BOOST_PP_LOCAL_ITERATE() +# endif + // all converters have been checked. Now we can do the + // precall part of the policy + if (!m_data.second().precall(args_)) + return 0; -/* ---------- pointers-to-members ---------- */ -#elif BOOST_PP_ITERATION_DEPTH() == 1 && BOOST_PP_ITERATION_FLAGS() == BOOST_PYTHON_POINTER_TO_MEMBER -// outer over cv-qualifiers + typedef typename detail::invoke_tag::type tag; -# define BOOST_PP_ITERATION_PARAMS_2 (3, (0, BOOST_PYTHON_MAX_ARITY, )) -# include BOOST_PP_ITERATE() + PyObject* result = detail::invoke( + tag(), result_converter(), m_data.first() BOOST_PP_ENUM_TRAILING_PARAMS(N, c)); + + return m_data.second().postcall(args_, result); + } + private: + compressed_pair m_data; + }; +}; -#elif BOOST_PP_ITERATION_DEPTH() == 2 -// inner over arities -#define N BOOST_PP_ITERATION() -#define Q BOOST_PYTHON_CV_QUALIFIER(BOOST_PP_RELATIVE_ITERATION(1)) -template < - class P, class R, class T - BOOST_PP_ENUM_TRAILING_PARAMS_Z(1, N, class A) - > -PyObject* operator()( - R (T::*pmf)(BOOST_PP_ENUM_PARAMS_Z(1, N, A)) Q - , PyObject* args, PyObject* keywords - , P const& policies - ) const -{ - return returning::call(pmf, args, keywords, &policies); -} +#endif // BOOST_PP_IS_ITERATING -#undef N -#undef Q -#endif diff --git a/include/boost/python/detail/invoke.hpp b/include/boost/python/detail/invoke.hpp new file mode 100644 index 00000000..4f59f44c --- /dev/null +++ b/include/boost/python/detail/invoke.hpp @@ -0,0 +1,130 @@ +#if !defined(BOOST_PP_IS_ITERATING) + +// 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 INVOKE_DWA20021122_HPP +# define INVOKE_DWA20021122_HPP + +# include +# include +# include +# include + +# include + +# include +# include +# include +# include +# include +# include + +// This file declares a series of overloaded invoke(...) functions, +// used to invoke wrapped C++ function (object)s from Python. Each one +// accepts: +// +// - a tag which identifies the invocation syntax (e.g. member +// functions must be invoked with a different syntax from regular +// functions) +// +// - a pointer to a result converter type, used solely as a way of +// transmitting the type of the result converter to the function (or +// an int, if the return type is void). +// +// - the "function", which may be a function object, a function or +// member function pointer, or a defaulted_virtual_fn. +// +// - The arg_from_python converters for each of the arguments to be +// passed to the function being invoked. + +namespace boost { namespace python { namespace detail { + +// This "result converter" is really just used as a dispatch tag to +// invoke(...), selecting the appropriate implementation +typedef int void_result_to_python; + +// Trait forward declaration. +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. +template +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 + > +{}; + +# define BOOST_PP_ITERATION_PARAMS_1 \ + (3, (0, BOOST_PYTHON_MAX_ARITY, )) +# include BOOST_PP_ITERATE() + +}}} // namespace boost::python::detail + +# endif // INVOKE_DWA20021122_HPP +#else + +# define N BOOST_PP_ITERATION() + +template +inline PyObject* invoke(fn_tag, RC*, F& f BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, AC, & ac) ) +{ + return RC()(f( BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, ac, () BOOST_PP_INTERCEPT) )); +} + +template +inline PyObject* invoke(fn_tag, void_result_to_python, F& f BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, AC, & ac) ) +{ + f( BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, ac, () BOOST_PP_INTERCEPT) ); + return none(); +} + +template +inline PyObject* invoke(mem_fn_tag, RC*, F& f, TC& tc BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, AC, & ac) ) +{ + return RC()( (tc().*f)(BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, ac, () BOOST_PP_INTERCEPT)) ); +} + +template +inline PyObject* invoke(mem_fn_tag, void_result_to_python, F& f, TC& tc BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, AC, & ac) ) +{ + (tc().*f)(BOOST_PP_ENUM_BINARY_PARAMS_Z(1, N, ac, () BOOST_PP_INTERCEPT)); + 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/make_keyword_range_fn.hpp b/include/boost/python/detail/make_keyword_range_fn.hpp index e807f158..b7d78fdd 100644 --- a/include/boost/python/detail/make_keyword_range_fn.hpp +++ b/include/boost/python/detail/make_keyword_range_fn.hpp @@ -6,38 +6,48 @@ #ifndef MAKE_KEYWORD_RANGE_FN_DWA2002927_HPP # define MAKE_KEYWORD_RANGE_FN_DWA2002927_HPP +# include # include -# include -# include + # include +# include + + namespace boost { namespace python { namespace detail { +// Think of this as a version of make_function without a compile-time +// check that the size of kw is no greater than the expected arity of +// F. This version is needed when defining functions with default +// arguments, because compile-time information about the number of +// keywords is missing for all but the initial function definition. template object make_keyword_range_function(F f, Policies const& policies, keyword_range const& kw) { - enum { n_arguments = detail::arg_tuple_size::value }; - return objects::function_object( - ::boost::bind(detail::caller(), f, _1, _2, policies) - , n_arguments - , kw); + return detail::make_function_aux( + f, policies, args_from_python(), detail::get_signature(f), kw, mpl::int_c<0>()); } -template +// Builds an '__init__' function which inserts the given Holder type +// in a wrapped C++ class instance. ArgList is an MPL type sequence +// describing the C++ argument types to be passed to Holder's +// constructor. +// +// Holder and ArgList are intended to be explicitly specified. +template object make_keyword_range_constructor( - Policies const& policies - , detail::keyword_range const& kw - , HolderGenerator* = 0 + CallPolicies const& policies // The CallPolicies with which to invoke the Holder's constructor + , detail::keyword_range const& kw // The (possibly empty) set of associated argument keywords + , Holder* = 0 , ArgList* = 0) { - enum { nargs = mpl::size::value }; + BOOST_STATIC_CONSTANT(unsigned, arity = mpl::size::value); - return objects::function_object( - ::boost::bind(detail::caller(), - objects::make_holder - ::template apply::execute - , _1, _2, policies) - , nargs + 1, kw); + return detail::make_keyword_range_function( + objects::make_holder + ::template apply::execute + , policies + , kw); } }}} // namespace boost::python::detail diff --git a/include/boost/python/detail/this_arg_from_python.hpp b/include/boost/python/detail/this_arg_from_python.hpp new file mode 100644 index 00000000..aa8c6c7b --- /dev/null +++ b/include/boost/python/detail/this_arg_from_python.hpp @@ -0,0 +1,122 @@ +// 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 THIS_ARG_FROM_PYTHON_DWA20021122_HPP +# define THIS_ARG_FROM_PYTHON_DWA20021122_HPP + +# include + +# include + +# include + +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)) + {} + + 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()(); + } + }; + + // 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; + }; + }; + + // 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 +{ + defaulted_virtual_fn(VirtualFunction dispatch, Default const& default_impl) + : dispatch(dispatch), default_impl(default_impl) {}; + + VirtualFunction dispatch; + Default default_impl; +}; + + + +# ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +template +struct is_defaulted_virtual_fn +{ BOOST_STATIC_CONSTANT(bool, value = false); }; + +template +struct is_defaulted_virtual_fn > +{ BOOST_STATIC_CONSTANT(bool, value = true); }; +# else +template +struct is_defaulted_virtual_fn +{ + template + static char helper(defaulted_virtual_fn* x); + static char (& helper(...)) [2]; + + BOOST_STATIC_CONSTANT(bool, value = sizeof(helper((T*)0)) == 1); + BOOST_MPL_AUX_LAMBDA_SUPPORT(1,is_defaulted_virtual_fn,(T)) +}; +# endif + +}}} // namespace boost::python::detail + +#endif // THIS_ARG_FROM_PYTHON_DWA20021122_HPP diff --git a/include/boost/python/make_function.hpp b/include/boost/python/make_function.hpp index 2277371f..99bc0253 100644 --- a/include/boost/python/make_function.hpp +++ b/include/boost/python/make_function.hpp @@ -6,74 +6,98 @@ #ifndef MAKE_FUNCTION_DWA20011221_HPP # define MAKE_FUNCTION_DWA20011221_HPP -# include -# include -# include -# include -# include -# include -# include # include +# include +# include + +# include + +# include +# include namespace boost { namespace python { +namespace detail +{ + // make_function_aux -- + // + // These helper functions for make_function (below) do the raw work + // of constructing a Python object from some invokable entity. See + // for more information about how + // the ConverterGenerators and Sig arguments are used. + template + object make_function_aux( + F f // An object that can be invoked by detail::invoke() + , CallPolicies const& p // CallPolicies to use in the invocation + , ConverterGenerators const& // An MPL iterator over a sequence of arg_from_python generators + , Sig const& // An MPL sequence of argument types expected by F + ) + { + return objects::function_object( + detail::caller(f, p) + , mpl::size::value - 1); + } + + // As above, except that it accepts argument keywords. NumKeywords + // is used only for a compile-time assertion to make sure the user + // doesn't pass more keywords than the function can accept. To + // disable all checking, pass mpl::int_c<0> for NumKeywords. + template + object make_function_aux( + F f + , CallPolicies const& p + , ConverterGenerators const& + , Sig const& + , detail::keyword_range const& kw // a [begin,end) pair of iterators over keyword names + , NumKeywords // An MPL integral type wrapper: the size of kw + ) + { + enum { arity = mpl::size::value - 1 }; + + typedef typename detail::error::more_keywords_than_function_arguments< + NumKeywords::value, arity + >::too_many_keywords assertion; + + return objects::function_object( + detail::caller(f, p) + , arity + , kw); + } +} + +// make_function -- +// +// These overloaded functions wrap a function or member function +// pointer as a Python object, using optional CallPolicies and +// Keywords. template object make_function(F f) { - return objects::function_object( - ::boost::bind(detail::caller(), f, _1, _2, default_call_policies()) - , detail::arg_tuple_size::value); + return detail::make_function_aux( + f,default_call_policies(),detail::args_from_python(), detail::get_signature(f)); } -template -object make_function(F f, Policies const& policies) +template +object make_function(F f, CallPolicies const& policies) { - return objects::function_object( - ::boost::bind(detail::caller(), f, _1, _2, policies) - , detail::arg_tuple_size::value); + return detail::make_function_aux( + f,policies,detail::args_from_python(), detail::get_signature(f)); } -template -object make_function(F f, Policies const& policies, Keywords const& keywords) +template +object make_function(F f, CallPolicies const& policies, Keywords const& keywords) { - enum { n_arguments = detail::arg_tuple_size::value }; - typedef typename detail::error::more_keywords_than_function_arguments< - Keywords::size, n_arguments - >::too_many_keywords assertion; - - return objects::function_object( - ::boost::bind(detail::caller(), f, _1, _2, policies) - , n_arguments - , keywords.range()); + return detail::make_function_aux( + f + , policies + , detail::args_from_python() + , detail::get_signature(f) + , keywords.range() + , mpl::int_c() + ); } -template -object make_constructor(HolderGenerator* = 0, ArgList* = 0) -{ - enum { nargs = mpl::size::value }; - - return objects::function_object( - ::boost::bind( - detail::caller() - , objects::make_holder - ::template apply::execute - , _1, _2, default_call_policies()) - , nargs + 1); -} +}} -template -object make_constructor(Policies const& policies, HolderGenerator* = 0, ArgList* = 0) -{ - enum { nargs = mpl::size::value }; - - return objects::function_object( - ::boost::bind(detail::caller(), - objects::make_holder - ::template apply::execute - , _1, _2, policies) - , nargs + 1); -} - -}} // namespace boost::python #endif // MAKE_FUNCTION_DWA20011221_HPP diff --git a/include/boost/python/object/function_handle.hpp b/include/boost/python/object/function_handle.hpp index f29c2e35..96cf82a5 100644 --- a/include/boost/python/object/function_handle.hpp +++ b/include/boost/python/object/function_handle.hpp @@ -7,10 +7,9 @@ # define FUNCTION_HANDLE_DWA2002725_HPP # include # include -# include # include # include -# include +# include namespace boost { namespace python { namespace objects { @@ -19,10 +18,16 @@ BOOST_PYTHON_DECL handle<> function_handle_impl(py_function const& f, unsigned m // Just like function_object, but returns a handle<> instead. Using // this for arg_to_python<> allows us to break a circular dependency // between object and arg_to_python. -template -inline handle<> function_handle(F const& f, unsigned min_args, unsigned max_args = 0) +template +inline handle<> function_handle(F const& f, Signature) { - return objects::function_handle_impl(objects::py_function(f), min_args, max_args); + enum { n_arguments = mpl::size::value - 1 }; + + return objects::function_handle_impl( + python::detail::caller< + F,python::detail::args_from_python,default_call_policies,Signature>( + f, default_call_policies()) + , n_arguments, n_arguments); } // Just like make_function, but returns a handle<> intead. Same @@ -30,9 +35,7 @@ inline handle<> function_handle(F const& f, unsigned min_args, unsigned max_args template handle<> make_function_handle(F f) { - return objects::function_handle( - ::boost::bind(python::detail::caller(), f, _1, _2, default_call_policies()) - , python::detail::arg_tuple_size::value); + return objects::function_handle(f, python::detail::get_signature(f)); } }}} // namespace boost::python::objects diff --git a/src/converter/arg_to_python_base.cpp b/src/converter/arg_to_python_base.cpp index 1740f6ee..6b143e64 100644 --- a/src/converter/arg_to_python_base.cpp +++ b/src/converter/arg_to_python_base.cpp @@ -12,26 +12,23 @@ namespace boost { namespace python { namespace converter { -namespace +PyObject* registration::to_python(void const volatile* source) const { - inline PyObject* convert_to_python(void const volatile* source, registration const& converters) + if (this->m_to_python == 0) { - if (converters.to_python == 0) - { - handle<> msg( - ::PyString_FromFormat( - "No to_python (by-value) converter found for C++ type: %s" - , converters.target_type.name())); + handle<> msg( + ::PyString_FromFormat( + "No to_python (by-value) converter found for C++ type: %s" + , this->target_type.name())); - PyErr_SetObject(PyExc_TypeError, msg.get()); + PyErr_SetObject(PyExc_TypeError, msg.get()); - throw_error_already_set(); - } - - return source == 0 - ? incref(Py_None) - : converters.to_python(const_cast(source)); + throw_error_already_set(); } + + return source == 0 + ? incref(Py_None) + : this->m_to_python(const_cast(source)); } namespace detail @@ -39,10 +36,11 @@ namespace detail arg_to_python_base::arg_to_python_base( void const volatile* source, registration const& converters) # if !defined(BOOST_MSVC) || BOOST_MSVC <= 1300 || _MSC_FULL_VER > 13102179 - : handle<>(converter::convert_to_python(source, converters)) + : handle<> # else - : m_ptr(converter::convert_to_python(source, converters)) -# endif + : m_ptr +# endif + (converters.to_python(source)) { } diff --git a/src/converter/registry.cpp b/src/converter/registry.cpp index 8c495106..87851eed 100644 --- a/src/converter/registry.cpp +++ b/src/converter/registry.cpp @@ -69,7 +69,7 @@ namespace registry # ifdef BOOST_PYTHON_TRACE_REGISTRY std::cout << "inserting to_python " << source_t << "\n"; # endif - to_python_function_t& slot = get(source_t)->to_python; + to_python_function_t& slot = get(source_t)->m_to_python; assert(slot == 0); // we have a problem otherwise if (slot != 0) diff --git a/test/m1.cpp b/test/m1.cpp index 60498d2a..463b51ae 100644 --- a/test/m1.cpp +++ b/test/m1.cpp @@ -199,6 +199,9 @@ D take_d_shared_ptr(boost::shared_ptr d) { return *d; } boost::shared_ptr d_factory() { return boost::shared_ptr(new D); } +struct Unregistered {}; +Unregistered make_unregistered(int) { return Unregistered(); } + BOOST_PYTHON_MODULE(m1) { using namespace boost::python; @@ -222,6 +225,8 @@ BOOST_PYTHON_MODULE(m1) def("new_noddy", new_noddy); def("new_simple", new_simple); + def("make_unregistered", make_unregistered); + // Expose f() in all its variations def("f", f); def("f_mutable_ref", f_mutable_ref); diff --git a/test/newtest.py b/test/newtest.py index 7b68a260..5162486a 100644 --- a/test/newtest.py +++ b/test/newtest.py @@ -3,6 +3,17 @@ >>> from m2 import * + Prove that we get an appropriate error from trying to return a type + for which we have no registered to_python converter + +>>> try: +... make_unregistered(1) +... except TypeError, x: +... if not str(x).startswith('No to_python (by-value) converter found for C++ type'): +... print str(x) +... else: +... print 'expected a TypeError' + >>> n = new_noddy() >>> s = new_simple() >>> unwrap_int(n)