diff --git a/include/boost/python/converter/context_result_converter.hpp b/include/boost/python/converter/context_result_converter.hpp new file mode 100755 index 00000000..c93dba0f --- /dev/null +++ b/include/boost/python/converter/context_result_converter.hpp @@ -0,0 +1,18 @@ +// Copyright David Abrahams 2003. 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 CONTEXT_RESULT_CONVERTER_DWA2003917_HPP +# define CONTEXT_RESULT_CONVERTER_DWA2003917_HPP + +namespace boost { namespace python { namespace converter { + +// A ResultConverter base class used to indicate that this result +// converter should be constructed with the original Python argument +// list. +struct context_result_converter {}; + +}}} // namespace boost::python::converter + +#endif // CONTEXT_RESULT_CONVERTER_DWA2003917_HPP diff --git a/include/boost/python/default_call_policies.hpp b/include/boost/python/default_call_policies.hpp index 5cc72249..db4e4cd3 100644 --- a/include/boost/python/default_call_policies.hpp +++ b/include/boost/python/default_call_policies.hpp @@ -31,19 +31,21 @@ struct default_call_policies { // Ownership of this argument tuple will ultimately be adopted by // the caller. - static PyObject* precall(PyObject* args_) + template + static bool precall(ArgumentPackage const&) { - Py_INCREF(args_); - return args_; + return true; } // Pass the result through - static PyObject* postcall(PyObject* args_, PyObject* result) + template + static PyObject* postcall(ArgumentPackage const&, PyObject* result) { return result; } typedef default_result_converter result_converter; + typedef PyObject* argument_package; }; struct default_result_converter diff --git a/include/boost/python/detail/caller.hpp b/include/boost/python/detail/caller.hpp index 77b0d396..7682a91d 100644 --- a/include/boost/python/detail/caller.hpp +++ b/include/boost/python/detail/caller.hpp @@ -9,14 +9,6 @@ # ifndef CALLER_DWA20021121_HPP # define CALLER_DWA20021121_HPP -# include - -# include -# include -# include -# include -# include - # include # include @@ -25,6 +17,7 @@ # include # include +# include # include # include @@ -34,8 +27,39 @@ # include # include +# include + +# include +# include + +# include +# include +# include +# include +# include +# include + namespace boost { namespace python { namespace detail { +# if 0 // argpkg +template +inline PyObject* get(N, PyObject* const& args_) +{ + return PyTuple_GET_ITEM(args_,N::value); +} +# else +template +inline PyObject* get(PyObject* const& args_ BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(unsigned,N)) +{ + return PyTuple_GET_ITEM(args_,N); +} +# endif + +inline unsigned arity(PyObject* const& args_) +{ + return PyTuple_GET_SIZE(args_); +} + // This "result converter" is really just used as // a dispatch tag to invoke(...), selecting the appropriate // implementation @@ -46,15 +70,34 @@ typedef int void_result_to_python; // converting the result to python. template struct select_result_converter - : mpl::if_< + : mpl::apply_if< is_same - , void_result_to_python - , typename mpl::apply1::type* + , mpl::identity + , mpl::apply1 > { }; - +template +inline ResultConverter create_result_converter( + ArgPackage const& args_ + , ResultConverter* + , converter::context_result_converter* +) +{ + return ResultConverter(args_); +} + +template +inline ResultConverter create_result_converter( + ArgPackage const& args_ + , ResultConverter* + , ... +) +{ + return ResultConverter(); +} + template struct caller_arity; template @@ -63,12 +106,21 @@ struct caller; # 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; +# if 0 // argpkg # define BOOST_PYTHON_ARG_CONVERTER(n) \ BOOST_PYTHON_NEXT(typename first::next, arg_iter,n) \ typedef arg_from_python c_t##n; \ - c_t##n c##n(PyTuple_GET_ITEM(args_, n)); \ + c_t##n c##n(get(mpl::int_(), inner_args)); \ if (!c##n.convertible()) \ return 0; +# else +# define BOOST_PYTHON_ARG_CONVERTER(n) \ + BOOST_PYTHON_NEXT(typename first::next, arg_iter,n) \ + typedef arg_from_python c_t##n; \ + c_t##n c##n(get(inner_args)); \ + if (!c##n.convertible()) \ + return 0; +# endif # define BOOST_PP_ITERATION_PARAMS_1 \ (3, (0, BOOST_PYTHON_MAX_ARITY + 1, )) @@ -142,6 +194,10 @@ struct caller_arity typedef typename mpl::begin::type first; typedef typename first::type result_t; typedef typename select_result_converter::type result_converter; + typedef typename Policies::argument_package argument_package; + + argument_package inner_args(args_); + # if N # define BOOST_PP_LOCAL_MACRO(i) BOOST_PYTHON_ARG_CONVERTER(i) # define BOOST_PP_LOCAL_LIMITS (0, N-1) @@ -149,17 +205,15 @@ struct caller_arity # endif // all converters have been checked. Now we can do the // precall part of the policy - PyObject* inner_args = m_data.second().precall(args_); - if (inner_args == 0) + if (!m_data.second().precall(inner_args)) return 0; - // manage the inner arguments - handle<> keeper(allow_null(inner_args)); - - typedef typename detail::invoke_tag::type tag; - PyObject* result = detail::invoke( - tag(), result_converter(), m_data.first() BOOST_PP_ENUM_TRAILING_PARAMS(N, c)); + detail::invoke_tag() + , create_result_converter(args_, (result_converter*)0, (result_converter*)0) + , m_data.first() + BOOST_PP_ENUM_TRAILING_PARAMS(N, c) + ); return m_data.second().postcall(inner_args, result); } diff --git a/include/boost/python/detail/invoke.hpp b/include/boost/python/detail/invoke.hpp index ad75b16a..cda53fea 100644 --- a/include/boost/python/detail/invoke.hpp +++ b/include/boost/python/detail/invoke.hpp @@ -45,23 +45,19 @@ namespace boost { namespace python { namespace detail { // 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 {}; +template +struct invoke_tag_ {}; // A metafunction returning the appropriate tag type for invoking an -// object of type T. -template +// object of type F with return type R. +template struct invoke_tag - : mpl::if_< - is_member_function_pointer - , mem_fn_tag - , fn_tag + : invoke_tag_< + is_same::value + , is_member_function_pointer::value > -{}; +{ +}; # define BOOST_PP_ITERATION_PARAMS_1 \ (3, (0, BOOST_PYTHON_MAX_ARITY, )) @@ -75,26 +71,26 @@ struct invoke_tag # 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) ) +inline PyObject* invoke(invoke_tag_, RC const& 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) )); + 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) ) +template +inline PyObject* invoke(invoke_tag_, RC const&, 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) ) +inline PyObject* invoke(invoke_tag_, RC const& 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)) ); + 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) ) +template +inline PyObject* invoke(invoke_tag_, RC const&, 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(); diff --git a/include/boost/python/detail/signature.hpp b/include/boost/python/detail/signature.hpp index cc7ba42d..581ed767 100644 --- a/include/boost/python/detail/signature.hpp +++ b/include/boost/python/detail/signature.hpp @@ -9,15 +9,16 @@ # ifndef SIGNATURE_DWA20021121_HPP # define SIGNATURE_DWA20021121_HPP -# include +# include # include +# include + # include # include -# include - -# include +# include +# include namespace boost { namespace python { namespace detail { diff --git a/include/boost/python/make_constructor.hpp b/include/boost/python/make_constructor.hpp new file mode 100755 index 00000000..f2c07a9e --- /dev/null +++ b/include/boost/python/make_constructor.hpp @@ -0,0 +1,283 @@ +// Copyright David Abrahams 2001. 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_CONSTRUCTOR_DWA20011221_HPP +# define MAKE_CONSTRUCTOR_DWA20011221_HPP + +# include + +# include +# include +# include + +# include +# include +# include +# include + +# include +# include + +# include +# include +# include +# include + +namespace boost { namespace python { + +namespace detail +{ + template + struct install_holder : converter::context_result_converter + { + install_holder(PyObject* args_) + : m_self(PyTuple_GetItem(args_, 0)) {} + + PyObject* operator()(T x) const + { + dispatch(x, is_pointer()); + return none(); + } + + private: + template + void dispatch(U* x, mpl::true_) const + { + std::auto_ptr owner(x); + dispatch(owner, mpl::false_()); + } + + template + void dispatch(Ptr x, mpl::false_) const + { + typedef typename pointee::type value_type; + typedef objects::pointer_holder holder; + typedef objects::instance instance_t; + + void* memory = holder::allocate(this->m_self, offsetof(instance_t, storage), sizeof(holder)); + try { + (new (memory) holder(x))->install(this->m_self); + } + catch(...) { + holder::deallocate(this->m_self, memory); + throw; + } + } + + PyObject* m_self; + }; + + struct constructor_result_converter + { + template + struct apply + { + typedef install_holder type; + }; + }; + + template + struct offset_args + { + offset_args(BaseArgs base_) : base(base_) {} + BaseArgs base; + }; + +# if 0 + template + inline PyObject* get(N, offset_args const& args_) + { + return get(mpl::int_<(N::value+Offset::value)>(), args_.base); + } +# else + template + inline PyObject* get(offset_args const& args_ BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(unsigned,N)) + { + return get<(N + Offset::value)>(args_.base); + } +# endif + + template + inline unsigned arity(offset_args const& args_) + { + return arity(args_.base) - Offset::value; + } + + template + struct constructor_policy : BasePolicy_ + { + constructor_policy(BasePolicy_ base) : BasePolicy_(base) {} + + // If the BasePolicy_ supplied a result converter it would be + // ignored; issue an error if it's not the default. + BOOST_STATIC_ASSERT(( + is_same< + typename BasePolicy_::result_converter + , default_result_converter + >::value + )); + + typedef constructor_result_converter result_converter; + typedef offset_args > argument_package; + }; + + template + struct outer_constructor_signature + { + typedef typename mpl::pop_front::type inner_args; + typedef typename mpl::push_front::type outer_args; + typedef typename mpl::push_front::type type; + }; + + // ETI workaround + template <> + struct outer_constructor_signature + { + typedef int type; + }; + + // { make_constructor_aux + // + // These helper functions for make_constructor (below) do the raw work + // of constructing a Python object from some invokable entity. See + // for more information about how + // the Sig arguments is used. + template + object make_constructor_aux( + F f // An object that can be invoked by detail::invoke() + , CallPolicies const& p // CallPolicies to use in the invocation + , Sig const& // An MPL sequence of argument types expected by F + ) + { + typedef typename outer_constructor_signature::type outer_signature; + + typedef constructor_policy inner_policy; + + return objects::function_object( + objects::py_function( + detail::caller(f, inner_policy(p)) + , outer_signature() + ) + ); + } + + // 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_<0> for NumKeywords. + template + object make_constructor_aux( + F f + , CallPolicies const& p + , 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; + + typedef typename outer_constructor_signature::type outer_signature; + + typedef constructor_policy inner_policy; + + return objects::function_object( + objects::py_function( + detail::caller(f, inner_policy(p)) + , outer_signature() + ) + ); + } + // } + + + // { Helpers for make_constructor when called with 3 arguments. These + // dispatch functions are used to discriminate between the cases + // when the 3rd argument is keywords or when it is a signature. + // + template + object make_constructor_dispatch(F f, CallPolicies const& policies, Keywords const& kw, mpl::true_) + { + return detail::make_constructor_aux( + f + , policies + , detail::get_signature(f) + , kw.range() + , mpl::int_() + ); + } + + template + object make_constructor_dispatch(F f, CallPolicies const& policies, Signature const& sig, mpl::false_) + { + return detail::make_constructor_aux( + f + , policies + , sig + ); + } + // } +} + +// { These overloaded functions wrap a function or member function +// pointer as a Python object, using optional CallPolicies, +// Keywords, and/or Signature. +template +object make_constructor(F f) +{ + return detail::make_constructor_aux( + f,default_call_policies(), detail::get_signature(f)); +} + +template +object make_constructor(F f, CallPolicies const& policies) +{ + return detail::make_constructor_aux( + f, policies, detail::get_signature(f)); +} + +template +object make_constructor( + F f + , CallPolicies const& policies + , KeywordsOrSignature const& keywords_or_signature) +{ + typedef typename + detail::is_reference_to_keywords::type + is_kw; + + return detail::make_constructor_dispatch( + f + , policies + , keywords_or_signature + , is_kw() + ); +} + +template +object make_constructor( + F f + , CallPolicies const& policies + , Keywords const& kw + , Signature const& sig + ) +{ + return detail::make_constructor_aux( + f + , policies + , sig + , kw.range() + , mpl::int_() + ); +} +// } + +}} + + +#endif // MAKE_CONSTRUCTOR_DWA20011221_HPP diff --git a/include/boost/python/return_arg.hpp b/include/boost/python/return_arg.hpp index 8c4dc189..716c2664 100755 --- a/include/boost/python/return_arg.hpp +++ b/include/boost/python/return_arg.hpp @@ -7,10 +7,17 @@ # define RETURN_ARG_DWA2003719_HPP # include # include + # include # include + +# if 0 // argpkg +# include +# endif + # include # include + # include namespace boost { namespace python { @@ -67,8 +74,24 @@ struct return_arg : Base // think it is better to issue an error instead, cause it can // lead to confusions >::type result_converter; - - static PyObject* postcall(PyObject *args, PyObject* result); + + template + static PyObject* postcall(ArgumentPackage const& args, PyObject* result) + { + // In case of arg_pos == 0 we could simply return Base::postcall, + // but this is redundant + BOOST_STATIC_ASSERT(arg_pos > 0); + + result = Base::postcall(args,result); + if (!result) + return 0; + Py_DECREF(result); +# if 0 // argpkg + return incref( detail::get(mpl::int_(),args) ); +# else + return incref( detail::get<(arg_pos-1)>(args) ); +# endif + } }; template < @@ -78,23 +101,6 @@ struct return_self : return_arg<1,Base> {}; - -template -inline PyObject* -return_arg::postcall(PyObject *args, PyObject* result) -{ - // In case of arg_pos == 0 we could simply return Base::postcall, - // but this is redundant - BOOST_STATIC_ASSERT(arg_pos > 0); - - handle<> base_result(Base::postcall(args,result)); - - if(!base_result) - return 0; - - return incref(PyTuple_GetItem(args,arg_pos-1)); -} - }} // namespace boost::python #endif // RETURN_ARG_DWA2003719_HPP diff --git a/include/boost/python/with_custodian_and_ward.hpp b/include/boost/python/with_custodian_and_ward.hpp index 23cb5508..a7ee9844 100644 --- a/include/boost/python/with_custodian_and_ward.hpp +++ b/include/boost/python/with_custodian_and_ward.hpp @@ -10,78 +10,94 @@ # include # include +# include namespace boost { namespace python { -template +template < + std::size_t custodian + , std::size_t ward + , class BasePolicy_ = default_call_policies +> struct with_custodian_and_ward : BasePolicy_ { - static PyObject* precall(PyObject* args); + BOOST_STATIC_ASSERT(custodian != ward); + BOOST_STATIC_ASSERT(custodian > 0); + BOOST_STATIC_ASSERT(ward > 0); + + template + static bool precall(ArgumentPackage const& args_) + { + unsigned arity_ = detail::arity(args_); + if (custodian > arity_ || ward > arity_) + { + PyErr_SetString( + PyExc_IndexError + , "boost::python::with_custodian_and_ward: argument index out of range" + ); + return false; + } + +# if 0 // argpkg + PyObject* patient = detail::get(mpl::int_<(ward-1)>(), args_); + PyObject* nurse = detail::get(mpl::int_<(custodian-1)>(), args_); +# else + PyObject* patient = detail::get<(ward-1)>(args_); + PyObject* nurse = detail::get<(custodian-1)>(args_); +# endif + PyObject* life_support = python::objects::make_nurse_and_patient(nurse, patient); + if (life_support == 0) + return false; + + bool result = BasePolicy_::precall(args_); + + if (!result) + Py_DECREF(life_support); + + return result; + } }; template struct with_custodian_and_ward_postcall : BasePolicy_ { - static PyObject* postcall(PyObject* args, PyObject* result); + BOOST_STATIC_ASSERT(custodian != ward); + + template + static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) + { + unsigned arity_ = detail::arity(args_); + if ( custodian > arity_ || ward > arity_ ) + { + PyErr_SetString( + PyExc_IndexError + , "boost::python::with_custodian_and_ward_postcall: argument index out of range" + ); + return 0; + } + +# if 0 // argpkg + PyObject* patient = ward > 0 ? detail::get(mpl::int_<(ward-1)>(),args_) : result; + PyObject* nurse = custodian > 0 ? detail::get(mpl::int_<(custodian-1)>(),args_) : result; +# else + PyObject* patient = ward > 0 ? detail::get<(ward-1)>(args_) : result; + PyObject* nurse = custodian > 0 ? detail::get<(custodian-1)>(args_) : result; +# endif + if (nurse == 0) return 0; + + result = BasePolicy_::postcall(args_, result); + if (result == 0) + return 0; + + if (python::objects::make_nurse_and_patient(nurse, patient) == 0) + { + Py_XDECREF(result); + return 0; + } + return result; + } }; -// -// implementations -// -template -PyObject* -with_custodian_and_ward::precall( - PyObject* args_ -) -{ - BOOST_STATIC_ASSERT(custodian != ward); - BOOST_STATIC_ASSERT(custodian > 0); - BOOST_STATIC_ASSERT(ward > 0); - - PyObject* patient = PyTuple_GetItem(args_, ward - 1); - if (patient == 0) - return 0; - - PyObject* nurse = PyTuple_GetItem(args_, custodian - 1); - if (nurse == 0) - return 0; - - PyObject* life_support = python::objects::make_nurse_and_patient(nurse, patient); - if (life_support == 0) - return 0; - - args_ = BasePolicy_::precall(args_); - if (args_ == 0) - Py_XDECREF(life_support); - - return args_; -} - -template -PyObject* -with_custodian_and_ward_postcall::postcall( - PyObject* args_ - , PyObject* result) -{ - BOOST_STATIC_ASSERT(custodian != ward); - - PyObject* patient = ward > 0 ? PyTuple_GetItem(args_, ward - 1) : result; - if (patient == 0) return 0; - - PyObject* nurse = custodian > 0 ? PyTuple_GetItem(args_, custodian - 1) : result; - if (nurse == 0) return 0; - - result = BasePolicy_::postcall(args_, result); - if (result == 0) - return 0; - - if (python::objects::make_nurse_and_patient(nurse, patient) == 0) - { - Py_XDECREF(result); - return 0; - } - return result; -} }} // namespace boost::python diff --git a/test/Jamfile b/test/Jamfile index 0e0d3a32..af1bec54 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -67,6 +67,7 @@ bpl-test crossmod_exception : crossmod_exception.py crossmod_exception_a.cpp crossmod_exception_b.cpp ; +bpl-test injected ; bpl-test properties ; bpl-test return_arg ; bpl-test staticmethod ; diff --git a/test/injected.cpp b/test/injected.cpp new file mode 100755 index 00000000..c78dc259 --- /dev/null +++ b/test/injected.cpp @@ -0,0 +1,35 @@ +// Copyright David Abrahams 2003. 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 "test_class.hpp" +#include +#include +#include + +using namespace boost::python; + +typedef test_class<> X; + +X* empty() { return new X(1000); } + +std::auto_ptr sum(int a, int b) { return std::auto_ptr(new X(a+b)); } + +boost::shared_ptr product(int a, int b, int c) +{ + return boost::shared_ptr(new X(a*b*c)); +} + +BOOST_PYTHON_MODULE(injected_ext) +{ + class_("X", init()) + .def("__init__", make_constructor(empty)) + .def("__init__", make_constructor(sum)) + .def("__init__", make_constructor(product)) + .def("value", &X::value) + ; +} diff --git a/test/injected.py b/test/injected.py new file mode 100644 index 00000000..3507b6e1 --- /dev/null +++ b/test/injected.py @@ -0,0 +1,25 @@ +''' +>>> from injected_ext import * +>>> X(3,5).value() - (3+5) +0 +>>> X(3,5,7).value() - (3*5*7) +0 +>>> X().value() +1000 +''' + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + status = run()[0] + if (status == 0): print "Done." + sys.exit(status) +