From 719c1733ddea0c5c9b5b6f5fb2d14980b6a0012f Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Sun, 27 Nov 2016 18:19:34 +0100 Subject: [PATCH] Split up tuple caster and function argument loader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed in order to allow the tuple caster to accept any sequence while keeping the argument loader fast. There is also very little overlap between the two classes which makes the separation clean. It’s also good practice not to have completely new functionality in a specialization. --- include/pybind11/cast.h | 145 +++++++++++++++++++--------------- include/pybind11/common.h | 3 + include/pybind11/functional.h | 2 +- include/pybind11/pybind11.h | 10 +-- 4 files changed, 92 insertions(+), 68 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 3486af3b..42d381bb 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -773,93 +773,51 @@ protected: }; template class type_caster> { - typedef std::tuple type; - typedef std::tuple...> itype; - typedef std::tuple args_type; - typedef std::tuple args_kwargs_type; + using type = std::tuple; + using indices = typename make_index_sequence::type; + static constexpr auto size = sizeof...(Tuple); + public: - enum { size = sizeof...(Tuple) }; - - static constexpr const bool has_kwargs = std::is_same::value; - static constexpr const bool has_args = has_kwargs || std::is_same::value; - bool load(handle src, bool convert) { if (!src || !PyTuple_Check(src.ptr()) || PyTuple_GET_SIZE(src.ptr()) != size) return false; - return load(src, convert, typename make_index_sequence::type()); - } - - template ::value && - !std::is_same::value, int> = 0> - bool load_args(handle args, handle, bool convert) { - return load(args, convert, typename make_index_sequence::type()); - } - - template ::value, int> = 0> - bool load_args(handle args, handle, bool convert) { - std::get<0>(value).load(args, convert); - return true; - } - - template ::value, int> = 0> - bool load_args(handle args, handle kwargs, bool convert) { - std::get<0>(value).load(args, convert); - std::get<1>(value).load(kwargs, convert); - return true; + return load_impl(src, convert, indices{}); } static handle cast(const type &src, return_value_policy policy, handle parent) { - return cast(src, policy, parent, typename make_index_sequence::type()); - } - - static PYBIND11_DESCR element_names() { - return detail::concat(make_caster::name()...); + return cast_impl(src, policy, parent, indices{}); } static PYBIND11_DESCR name() { - return type_descr(_("Tuple[") + element_names() + _("]")); - } - - template enable_if_t::value, ReturnValue> call(Func &&f) { - return call(std::forward(f), typename make_index_sequence::type()); - } - - template enable_if_t::value, void_type> call(Func &&f) { - call(std::forward(f), typename make_index_sequence::type()); - return void_type(); + return type_descr(_("Tuple[") + detail::concat(make_caster::name()...) + _("]")); } template using cast_op_type = type; - operator type() { - return cast(typename make_index_sequence::type()); - } + operator type() { return implicit_cast(indices{}); } protected: - template ReturnValue call(Func &&f, index_sequence) { - return f(cast_op(std::get(value))...); - } + template + type implicit_cast(index_sequence) { return type(cast_op(std::get(value))...); } - template type cast(index_sequence) { - return type(cast_op(std::get(value))...); - } + static constexpr bool load_impl(handle, bool, index_sequence<>) { return true; } - template bool load(handle src, bool convert, index_sequence) { - std::array success {{ - std::get(value).load(PyTuple_GET_ITEM(src.ptr(), Indices), convert)... - }}; - (void) convert; /* avoid a warning when the tuple is empty */ - for (bool r : success) + template + bool load_impl(handle src, bool convert, index_sequence) { + for (bool r : {std::get(value).load(PyTuple_GET_ITEM(src.ptr(), Is), convert)...}) if (!r) return false; return true; } + static handle cast_impl(const type &, return_value_policy, handle, + index_sequence<>) { return tuple().release(); } + /* Implementation: Convert a C++ tuple into a Python tuple */ - template static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence) { + template + static handle cast_impl(const type &src, return_value_policy policy, handle parent, index_sequence) { std::array entries {{ - reinterpret_steal(make_caster::cast(std::get(src), policy, parent))... + reinterpret_steal(make_caster::cast(std::get(src), policy, parent))... }}; for (const auto &entry: entries) if (!entry) @@ -1239,6 +1197,69 @@ constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } } NAMESPACE_BEGIN(detail) + +/// Helper class which loads arguments for C++ functions called from Python +template +class argument_loader { + using itypes = type_list...>; + using indices = typename make_index_sequence::type; + +public: + static constexpr auto has_kwargs = std::is_same>::value; + static constexpr auto has_args = has_kwargs || std::is_same>::value; + + static PYBIND11_DESCR arg_names() { return detail::concat(make_caster::name()...); } + + bool load_args(handle args, handle kwargs, bool convert) { + return load_impl(args, kwargs, convert, itypes{}); + } + + template + enable_if_t::value, Return> call(Func &&f) { + return call_impl(std::forward(f), indices{}); + } + + template + enable_if_t::value, void_type> call(Func &&f) { + call_impl(std::forward(f), indices{}); + return void_type(); + } + +private: + bool load_impl(handle args_, handle, bool convert, type_list) { + std::get<0>(value).load(args_, convert); + return true; + } + + bool load_impl(handle args_, handle kwargs_, bool convert, type_list) { + std::get<0>(value).load(args_, convert); + std::get<1>(value).load(kwargs_, convert); + return true; + } + + bool load_impl(handle args, handle, bool convert, ... /* anything else */) { + return load_impl_sequence(args, convert, indices{}); + } + + static constexpr bool load_impl_sequence(handle, bool, index_sequence<>) { return true; } + + template + bool load_impl_sequence(handle src, bool convert, index_sequence) { + for (bool r : {std::get(value).load(PyTuple_GET_ITEM(src.ptr(), Is), convert)...}) + if (!r) + return false; + return true; + } + + template + Return call_impl(Func &&f, index_sequence) { + return std::forward(f)(cast_op(std::get(value))...); + } + +private: + std::tuple...> value; +}; + NAMESPACE_BEGIN(constexpr_impl) /// Implementation details for constexpr functions constexpr int first(int i) { return i; } diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 74dcbd92..5622ad95 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -368,6 +368,9 @@ template using intrinsic_t = typename intrinsic_type::type; /// Helper type to replace 'void' in some expressions struct void_type { }; +/// Helper template which holds a list of types +template struct type_list { }; + /// from __cpp_future__ import (convenient aliases from C++14/17) template using bool_constant = std::integral_constant; template using negation = bool_constant; diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index dbeea5dc..f1b0ebbb 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -70,7 +70,7 @@ public: } PYBIND11_TYPE_CASTER(type, _("Callable[[") + - type_caster>::element_names() + _("], ") + + argument_loader::arg_names() + _("], ") + type_caster::name() + _("]")); }; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c88273d5..b737dc7f 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -111,10 +111,10 @@ protected: } /* Type casters for the function arguments and return value */ - typedef detail::type_caster> cast_in; - typedef detail::type_caster::value, detail::void_type, - typename detail::intrinsic_type::type>::type> cast_out; + using cast_in = detail::argument_loader; + using cast_out = detail::make_caster< + detail::conditional_t::value, detail::void_type, Return> + >; /* Dispatch code which converts function arguments and performs the actual function call */ rec->impl = [](detail::function_record *rec, handle args, handle kwargs, handle parent) -> handle { @@ -151,7 +151,7 @@ protected: /* Generate a readable signature describing the function's arguments and return value types */ using detail::descr; using detail::_; - PYBIND11_DESCR signature = _("(") + cast_in::element_names() + _(") -> ") + cast_out::name(); + PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name(); /* Register the function with Python from generic (non-templated) code */ initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args));