Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 91 additions & 67 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -737,12 +737,12 @@ template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
typedef std::pair<T1, T2> type;
public:
bool load(handle src, bool convert) {
if (!src)
if (!isinstance<sequence>(src))
return false;
else if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != 2)
const auto seq = reinterpret_borrow<sequence>(src);
if (seq.size() != 2)
return false;
return first.load(PyTuple_GET_ITEM(src.ptr(), 0), convert) &&
second.load(PyTuple_GET_ITEM(src.ptr(), 1), convert);
return first.load(seq[0], convert) && second.load(seq[1], convert);
}

static handle cast(const type &src, return_value_policy policy, handle parent) {
Expand Down Expand Up @@ -773,93 +773,54 @@ template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
};

template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
typedef std::tuple<Tuple...> type;
typedef std::tuple<intrinsic_t<Tuple>...> itype;
typedef std::tuple<args> args_type;
typedef std::tuple<args, kwargs> args_kwargs_type;
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;
using type = std::tuple<Tuple...>;
using indices = make_index_sequence<sizeof...(Tuple)>;
static constexpr auto size = sizeof...(Tuple);

public:
bool load(handle src, bool convert) {
if (!src || !PyTuple_Check(src.ptr()) || PyTuple_GET_SIZE(src.ptr()) != size)
if (!isinstance<sequence>(src))
return false;
return load(src, convert, typename make_index_sequence<sizeof...(Tuple)>::type());
}

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;
const auto seq = reinterpret_borrow<sequence>(src);
if (seq.size() != size)
return false;
return load_impl(seq, convert, indices{});
}

static handle cast(const type &src, return_value_policy policy, handle parent) {
return cast(src, policy, parent, typename make_index_sequence<size>::type());
}

static PYBIND11_DESCR element_names() {
return detail::concat(make_caster<Tuple>::name()...);
return cast_impl(src, policy, parent, indices{});
}

static PYBIND11_DESCR name() {
return type_descr(_("Tuple[") + element_names() + _("]"));
}

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();
return type_descr(_("Tuple[") + detail::concat(make_caster<Tuple>::name()...) + _("]"));
}

template <typename T> using cast_op_type = type;

operator type() {
return cast(typename make_index_sequence<sizeof...(Tuple)>::type());
}
operator type() { return implicit_cast(indices{}); }

protected:
template <typename ReturnValue, typename Func, size_t ... Index> ReturnValue call(Func &&f, index_sequence<Index...>) {
return f(cast_op<Tuple>(std::get<Index>(value))...);
}
template <size_t... Is>
type implicit_cast(index_sequence<Is...>) { return type(cast_op<Tuple>(std::get<Is>(value))...); }

template <size_t ... Index> type cast(index_sequence<Index...>) {
return type(cast_op<Tuple>(std::get<Index>(value))...);
}
static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; }

template <size_t ... Indices> bool load(handle src, bool convert, index_sequence<Indices...>) {
std::array<bool, size> success {{
std::get<Indices>(value).load(PyTuple_GET_ITEM(src.ptr(), Indices), convert)...
}};
(void) convert; /* avoid a warning when the tuple is empty */
for (bool r : success)
template <size_t... Is>
bool load_impl(const sequence &seq, bool convert, index_sequence<Is...>) {
for (bool r : {std::get<Is>(value).load(seq[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 <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 {{
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)
if (!entry)
Expand Down Expand Up @@ -1239,6 +1200,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 <typename... Args>
class argument_loader {
using itypes = type_list<intrinsic_t<Args>...>;
using indices = make_index_sequence<sizeof...(Args)>;

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)
/// Implementation details for constexpr functions
constexpr int first(int i) { return i; }
Expand Down
13 changes: 11 additions & 2 deletions include/pybind11/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,15 @@ struct internals {
inline internals &get_internals();

/// Index sequence for convenient template metaprogramming involving tuples
#ifdef PYBIND11_CPP14
using std::index_sequence;
using std::make_index_sequence;
#else
template<size_t ...> struct index_sequence { };
template<size_t N, size_t ...S> struct make_index_sequence : make_index_sequence <N - 1, N - 1, S...> { };
template<size_t ...S> struct make_index_sequence <0, S...> { typedef index_sequence<S...> type; };
template<size_t N, size_t ...S> struct make_index_sequence_impl : make_index_sequence_impl <N - 1, N - 1, S...> { };
template<size_t ...S> struct make_index_sequence_impl <0, S...> { typedef index_sequence<S...> type; };
template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;
#endif

/// Strip the class from a method type
template <typename T> struct remove_class { };
Expand All @@ -368,6 +374,9 @@ template <typename T> using intrinsic_t = typename intrinsic_type<T>::type;
/// Helper type to replace 'void' in some expressions
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)
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <class T> using negation = bool_constant<!T::value>;
Expand Down
12 changes: 6 additions & 6 deletions include/pybind11/descr.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ template <size_t Size1, size_t Size2> class descr {
public:
constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1])
: descr(text, types,
typename make_index_sequence<Size1>::type(),
typename make_index_sequence<Size2>::type()) { }
make_index_sequence<Size1>(),
make_index_sequence<Size2>()) { }

constexpr const char *text() const { return m_text; }
constexpr const std::type_info * const * types() const { return m_types; }

template <size_t OtherSize1, size_t OtherSize2>
constexpr descr<Size1 + OtherSize1, Size2 + OtherSize2> operator+(const descr<OtherSize1, OtherSize2> &other) const {
return concat(other,
typename make_index_sequence<Size1>::type(),
typename make_index_sequence<Size2>::type(),
typename make_index_sequence<OtherSize1>::type(),
typename make_index_sequence<OtherSize2>::type());
make_index_sequence<Size1>(),
make_index_sequence<Size2>(),
make_index_sequence<OtherSize1>(),
make_index_sequence<OtherSize2>());
}

protected:
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ template <typename Return, typename... Args> struct type_caster<std::function<Re
}

PYBIND11_TYPE_CASTER(type, _("Callable[[") +
type_caster<std::tuple<Args...>>::element_names() + _("], ") +
argument_loader<Args...>::arg_names() + _("], ") +
type_caster<retval_type>::name() +
_("]"));
};
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/numpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ struct vectorize_helper {
explicit vectorize_helper(T&&f) : f(std::forward<T>(f)) { }

object operator()(array_t<Args, array::c_style | array::forcecast>... args) {
return run(args..., typename make_index_sequence<sizeof...(Args)>::type());
return run(args..., make_index_sequence<sizeof...(Args)>());
}

template <size_t ... Index> object run(array_t<Args, array::c_style | array::forcecast>&... args, index_sequence<Index...> index) {
Expand Down
10 changes: 5 additions & 5 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ class cpp_function : public function {
}

/* Type casters for the function arguments and return value */
typedef detail::type_caster<typename std::tuple<Args...>> cast_in;
typedef detail::type_caster<typename std::conditional<
std::is_void<Return>::value, detail::void_type,
typename detail::intrinsic_type<Return>::type>::type> cast_out;
using cast_in = detail::argument_loader<Args...>;
using cast_out = detail::make_caster<
detail::conditional_t<std::is_void<Return>::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 {
Expand Down Expand Up @@ -151,7 +151,7 @@ class cpp_function : public function {

/* 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));
Expand Down
3 changes: 3 additions & 0 deletions tests/test_python_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ def test_instance(capture):

assert instance.pair_passthrough((True, "test")) == ("test", True)
assert instance.tuple_passthrough((True, "test", 5)) == (5, "test", True)
# Any sequence can be cast to a std::pair or std::tuple
assert instance.pair_passthrough([True, "test"]) == ("test", True)
assert instance.tuple_passthrough([True, "test", 5]) == (5, "test", True)

assert instance.get_bytes_from_string().decode() == "foo"
assert instance.get_bytes_from_str().decode() == "bar"
Expand Down