Split up tuple caster and function argument loader

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.
This commit is contained in:
Dean Moldovan 2016-11-27 18:19:34 +01:00 committed by Wenzel Jakob
parent 9f13a30c6e
commit 719c1733dd
4 changed files with 92 additions and 68 deletions

View File

@ -773,93 +773,51 @@ protected:
}; };
template <typename... Tuple> class type_caster<std::tuple<Tuple...>> { template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
typedef std::tuple<Tuple...> type; using type = std::tuple<Tuple...>;
typedef std::tuple<intrinsic_t<Tuple>...> itype; using indices = typename make_index_sequence<sizeof...(Tuple)>::type;
typedef std::tuple<args> args_type; static constexpr auto size = sizeof...(Tuple);
typedef std::tuple<args, kwargs> args_kwargs_type;
public: public:
enum { size = sizeof...(Tuple) };
static constexpr const bool has_kwargs = std::is_same<itype, args_kwargs_type>::value;
static constexpr const bool has_args = has_kwargs || std::is_same<itype, args_type>::value;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!src || !PyTuple_Check(src.ptr()) || PyTuple_GET_SIZE(src.ptr()) != size) if (!src || !PyTuple_Check(src.ptr()) || PyTuple_GET_SIZE(src.ptr()) != size)
return false; return false;
return load(src, convert, typename make_index_sequence<sizeof...(Tuple)>::type()); return load_impl(src, convert, indices{});
}
template <typename T = itype, enable_if_t<
!std::is_same<T, args_type>::value &&
!std::is_same<T, args_kwargs_type>::value, int> = 0>
bool load_args(handle args, handle, bool convert) {
return load(args, convert, typename make_index_sequence<sizeof...(Tuple)>::type());
}
template <typename T = itype, enable_if_t<std::is_same<T, args_type>::value, int> = 0>
bool load_args(handle args, handle, bool convert) {
std::get<0>(value).load(args, convert);
return true;
}
template <typename T = itype, enable_if_t<std::is_same<T, args_kwargs_type>::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;
} }
static handle cast(const type &src, return_value_policy policy, handle parent) { static handle cast(const type &src, return_value_policy policy, handle parent) {
return cast(src, policy, parent, typename make_index_sequence<size>::type()); return cast_impl(src, policy, parent, indices{});
}
static PYBIND11_DESCR element_names() {
return detail::concat(make_caster<Tuple>::name()...);
} }
static PYBIND11_DESCR name() { static PYBIND11_DESCR name() {
return type_descr(_("Tuple[") + element_names() + _("]")); return type_descr(_("Tuple[") + detail::concat(make_caster<Tuple>::name()...) + _("]"));
}
template <typename ReturnValue, typename Func> enable_if_t<!std::is_void<ReturnValue>::value, ReturnValue> call(Func &&f) {
return call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
}
template <typename ReturnValue, typename Func> enable_if_t<std::is_void<ReturnValue>::value, void_type> call(Func &&f) {
call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
return void_type();
} }
template <typename T> using cast_op_type = type; template <typename T> using cast_op_type = type;
operator type() { operator type() { return implicit_cast(indices{}); }
return cast(typename make_index_sequence<sizeof...(Tuple)>::type());
}
protected: protected:
template <typename ReturnValue, typename Func, size_t ... Index> ReturnValue call(Func &&f, index_sequence<Index...>) { template <size_t... Is>
return f(cast_op<Tuple>(std::get<Index>(value))...); type implicit_cast(index_sequence<Is...>) { return type(cast_op<Tuple>(std::get<Is>(value))...); }
}
template <size_t ... Index> type cast(index_sequence<Index...>) { static constexpr bool load_impl(handle, bool, index_sequence<>) { return true; }
return type(cast_op<Tuple>(std::get<Index>(value))...);
}
template <size_t ... Indices> bool load(handle src, bool convert, index_sequence<Indices...>) { template <size_t... Is>
std::array<bool, size> success {{ bool load_impl(handle src, bool convert, index_sequence<Is...>) {
std::get<Indices>(value).load(PyTuple_GET_ITEM(src.ptr(), Indices), convert)... for (bool r : {std::get<Is>(value).load(PyTuple_GET_ITEM(src.ptr(), Is), convert)...})
}};
(void) convert; /* avoid a warning when the tuple is empty */
for (bool r : success)
if (!r) if (!r)
return false; return false;
return true; 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 */ /* Implementation: Convert a C++ tuple into a Python tuple */
template <size_t ... Indices> static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence<Indices...>) { template <size_t... Is>
static handle cast_impl(const type &src, return_value_policy policy, handle parent, index_sequence<Is...>) {
std::array<object, size> entries {{ std::array<object, size> entries {{
reinterpret_steal<object>(make_caster<Tuple>::cast(std::get<Indices>(src), policy, parent))... reinterpret_steal<object>(make_caster<Tuple>::cast(std::get<Is>(src), policy, parent))...
}}; }};
for (const auto &entry: entries) for (const auto &entry: entries)
if (!entry) if (!entry)
@ -1239,6 +1197,69 @@ constexpr arg operator"" _a(const char *name, size_t) { return arg(name); }
} }
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
/// Helper class which loads arguments for C++ functions called from Python
template <typename... Args>
class argument_loader {
using itypes = type_list<intrinsic_t<Args>...>;
using indices = typename make_index_sequence<sizeof...(Args)>::type;
public:
static constexpr auto has_kwargs = std::is_same<itypes, type_list<args, kwargs>>::value;
static constexpr auto has_args = has_kwargs || std::is_same<itypes, type_list<args>>::value;
static PYBIND11_DESCR arg_names() { return detail::concat(make_caster<Args>::name()...); }
bool load_args(handle args, handle kwargs, bool convert) {
return load_impl(args, kwargs, convert, itypes{});
}
template <typename Return, typename Func>
enable_if_t<!std::is_void<Return>::value, Return> call(Func &&f) {
return call_impl<Return>(std::forward<Func>(f), indices{});
}
template <typename Return, typename Func>
enable_if_t<std::is_void<Return>::value, void_type> call(Func &&f) {
call_impl<Return>(std::forward<Func>(f), indices{});
return void_type();
}
private:
bool load_impl(handle args_, handle, bool convert, type_list<args>) {
std::get<0>(value).load(args_, convert);
return true;
}
bool load_impl(handle args_, handle kwargs_, bool convert, type_list<args, kwargs>) {
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 <size_t... Is>
bool load_impl_sequence(handle src, bool convert, index_sequence<Is...>) {
for (bool r : {std::get<Is>(value).load(PyTuple_GET_ITEM(src.ptr(), Is), convert)...})
if (!r)
return false;
return true;
}
template <typename Return, typename Func, size_t... Is>
Return call_impl(Func &&f, index_sequence<Is...>) {
return std::forward<Func>(f)(cast_op<Args>(std::get<Is>(value))...);
}
private:
std::tuple<make_caster<Args>...> value;
};
NAMESPACE_BEGIN(constexpr_impl) NAMESPACE_BEGIN(constexpr_impl)
/// Implementation details for constexpr functions /// Implementation details for constexpr functions
constexpr int first(int i) { return i; } constexpr int first(int i) { return i; }

View File

@ -368,6 +368,9 @@ template <typename T> using intrinsic_t = typename intrinsic_type<T>::type;
/// Helper type to replace 'void' in some expressions /// Helper type to replace 'void' in some expressions
struct void_type { }; struct void_type { };
/// Helper template which holds a list of types
template <typename...> struct type_list { };
/// from __cpp_future__ import (convenient aliases from C++14/17) /// from __cpp_future__ import (convenient aliases from C++14/17)
template <bool B> using bool_constant = std::integral_constant<bool, B>; template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <class T> using negation = bool_constant<!T::value>; template <class T> using negation = bool_constant<!T::value>;

View File

@ -70,7 +70,7 @@ public:
} }
PYBIND11_TYPE_CASTER(type, _("Callable[[") + PYBIND11_TYPE_CASTER(type, _("Callable[[") +
type_caster<std::tuple<Args...>>::element_names() + _("], ") + argument_loader<Args...>::arg_names() + _("], ") +
type_caster<retval_type>::name() + type_caster<retval_type>::name() +
_("]")); _("]"));
}; };

View File

@ -111,10 +111,10 @@ protected:
} }
/* Type casters for the function arguments and return value */ /* Type casters for the function arguments and return value */
typedef detail::type_caster<typename std::tuple<Args...>> cast_in; using cast_in = detail::argument_loader<Args...>;
typedef detail::type_caster<typename std::conditional< using cast_out = detail::make_caster<
std::is_void<Return>::value, detail::void_type, detail::conditional_t<std::is_void<Return>::value, detail::void_type, Return>
typename detail::intrinsic_type<Return>::type>::type> cast_out; >;
/* Dispatch code which converts function arguments and performs the actual function call */ /* 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 { 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 */ /* Generate a readable signature describing the function's arguments and return value types */
using detail::descr; using detail::_; 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 */ /* Register the function with Python from generic (non-templated) code */
initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args)); initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args));