nicer type_caster::load() calling conventions

This commit is contained in:
Wenzel Jakob
2016-05-10 15:59:01 +01:00
parent 5984baafd0
commit 178c8a899d
12 changed files with 213 additions and 71 deletions

View File

@@ -92,7 +92,7 @@ struct function_record {
std::vector<argument_record> args;
/// Pointer to lambda function which converts arguments and performs the actual call
handle (*impl) (function_record *, handle, handle) = nullptr;
handle (*impl) (function_record *, handle, handle, handle) = nullptr;
/// Storage for the wrapped function pointer and captured data, if any
void *data[3] = { };
@@ -104,7 +104,16 @@ struct function_record {
return_value_policy policy = return_value_policy::automatic;
/// True if name == '__init__'
bool is_constructor = false;
bool is_constructor : 1;
/// True if the function has a '*args' argument
bool has_args : 1;
/// True if the function has a '**kwargs' argument
bool has_kwargs : 1;
/// Number of arguments
uint16_t nargs;
/// Python method object
PyMethodDef *def = nullptr;

View File

@@ -15,6 +15,7 @@
#include "descr.h"
#include <array>
#include <limits>
#include <iostream>
NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)
@@ -320,7 +321,9 @@ public:
bool load(handle src, bool) {
py_type py_value;
if (std::is_floating_point<T>::value) {
if (!src) {
return false;
} if (std::is_floating_point<T>::value) {
py_value = (py_type) PyFloat_AsDouble(src.ptr());
} else if (sizeof(T) <= sizeof(long)) {
if (std::is_signed<T>::value)
@@ -394,7 +397,9 @@ public:
using type_caster<void_type>::cast;
bool load(handle h, bool) {
if (h.ptr() == Py_None) {
if (!h) {
return false;
} else if (h.ptr() == Py_None) {
value = nullptr;
return true;
}
@@ -435,7 +440,8 @@ template <> class type_caster<std::nullptr_t> : public type_caster<void_type> {
template <> class type_caster<bool> {
public:
bool load(handle src, bool) {
if (src.ptr() == Py_True) { value = true; return true; }
if (!src) return false;
else if (src.ptr() == Py_True) { value = true; return true; }
else if (src.ptr() == Py_False) { value = false; return true; }
else return false;
}
@@ -450,7 +456,9 @@ public:
bool load(handle src, bool) {
object temp;
handle load_src = src;
if (PyUnicode_Check(load_src.ptr())) {
if (!src) {
return false;
} else if (PyUnicode_Check(load_src.ptr())) {
temp = object(PyUnicode_AsUTF8String(load_src.ptr()), false);
if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError
load_src = temp;
@@ -489,7 +497,9 @@ public:
bool load(handle src, bool) {
object temp;
handle load_src = src;
if (!PyUnicode_Check(load_src.ptr())) {
if (!src) {
return false;
} else if (!PyUnicode_Check(load_src.ptr())) {
temp = object(PyUnicode_FromObject(load_src.ptr()), false);
if (!temp) { PyErr_Clear(); return false; }
load_src = temp;
@@ -527,7 +537,7 @@ protected:
template <> class type_caster<char> : public type_caster<std::string> {
public:
bool load(handle src, bool convert) {
if (src.ptr() == Py_None) { return true; }
if (src.ptr() == Py_None) return true;
return type_caster<std::string>::load(src, convert);
}
@@ -550,7 +560,7 @@ public:
template <> class type_caster<wchar_t> : public type_caster<std::wstring> {
public:
bool load(handle src, bool convert) {
if (src.ptr() == Py_None) { return true; }
if (src.ptr() == Py_None) return true;
return type_caster<std::wstring>::load(src, convert);
}
@@ -574,7 +584,9 @@ 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 (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != 2)
if (!src)
return false;
else if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != 2)
return false;
return first.load(PyTuple_GET_ITEM(src.ptr(), 0), convert) &&
second.load(PyTuple_GET_ITEM(src.ptr(), 1), convert);
@@ -610,13 +622,41 @@ protected:
template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
typedef std::tuple<Tuple...> type;
typedef std::tuple<typename intrinsic_type<Tuple>::type...> 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;
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<sizeof...(Tuple)>::type());
}
template <typename T = itype, typename std::enable_if<
!std::is_same<T, args_type>::value &&
!std::is_same<T, args_kwargs_type>::value, int>::type = 0>
bool load_args(handle args, handle, bool convert) {
return load(args, convert, typename make_index_sequence<sizeof...(Tuple)>::type());
}
template <typename T = itype, typename std::enable_if<std::is_same<T, args_type>::value, int>::type = 0>
bool load_args(handle args, handle, bool convert) {
std::get<0>(value).load(args, convert);
return true;
}
template <typename T = itype, typename std::enable_if<std::is_same<T, args_kwargs_type>::value, int>::type = 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) {
return cast(src, policy, parent, typename make_index_sequence<size>::type());
}
@@ -655,10 +695,8 @@ protected:
}
template <size_t ... Indices> bool load(handle src, bool convert, index_sequence<Indices...>) {
if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != size)
return false;
std::array<bool, size> success {{
(PyTuple_GET_ITEM(src.ptr(), Indices) != nullptr ? std::get<Indices>(value).load(PyTuple_GET_ITEM(src.ptr(), Indices), convert) : false)...
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)
@@ -742,14 +780,16 @@ protected:
template <typename T> struct handle_type_name { static PYBIND11_DESCR name() { return _<T>(); } };
template <> struct handle_type_name<bytes> { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } };
template <> struct handle_type_name<args> { static PYBIND11_DESCR name() { return _("*args"); } };
template <> struct handle_type_name<kwargs> { static PYBIND11_DESCR name() { return _("**kwargs"); } };
template <typename type>
struct type_caster<type, typename std::enable_if<std::is_base_of<handle, type>::value>::type> {
public:
template <typename T = type, typename std::enable_if<std::is_same<T, handle>::value, int>::type = 0>
bool load(handle src, bool /* convert */) { value = src; return value.check(); }
template <typename T = type, typename std::enable_if<!std::is_base_of<object, T>::value, int>::type = 0>
bool load(handle src, bool /* convert */) { value = type(src); return value.check(); }
template <typename T = type, typename std::enable_if<!std::is_same<T, handle>::value, int>::type = 0>
template <typename T = type, typename std::enable_if<std::is_base_of<object, T>::value, int>::type = 0>
bool load(handle src, bool /* convert */) { value = type(src, true); return value.check(); }
static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
@@ -768,7 +808,9 @@ template <typename T> T cast(handle handle) {
return conv.operator typename type_caster::template cast_op_type<T>();
}
template <typename T> object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
template <typename T> object cast(const T &value,
return_value_policy policy = return_value_policy::automatic_reference,
handle parent = handle()) {
if (policy == return_value_policy::automatic)
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
else if (policy == return_value_policy::automatic_reference)
@@ -808,14 +850,14 @@ template <typename... Args> object handle::call(Args &&... args) const {
return operator()(std::forward<Args>(args)...);
}
inline object handle::operator()(detail::args args) const {
inline object handle::operator()(detail::args_proxy args) const {
object result(PyObject_CallObject(m_ptr, args.ptr()), false);
if (!result)
throw error_already_set();
return result;
}
inline object handle::operator()(detail::args args, detail::kwargs kwargs) const {
inline object handle::operator()(detail::args_proxy args, detail::kwargs_proxy kwargs) const {
object result(PyObject_Call(m_ptr, args.ptr(), kwargs.ptr()), false);
if (!result)
throw error_already_set();

View File

@@ -142,7 +142,7 @@ NAMESPACE_BEGIN(pybind11)
typedef Py_ssize_t ssize_t;
/// Approach used to cast a previously unknown C++ instance into a Python object
enum class return_value_policy : int {
enum class return_value_policy : uint8_t {
/** This is the default return value policy, which falls back to the policy
return_value_policy::take_ownership when the return value is a pointer.
Otherwise, it uses return_value::move or return_value::copy for rvalue

View File

@@ -26,6 +26,8 @@ NAMESPACE_BEGIN(detail)
template <typename T> class type_caster<std::complex<T>> {
public:
bool load(handle src, bool) {
if (!src)
return false;
Py_complex result = PyComplex_AsCComplex(src.ptr());
if (result.real == -1.0 && PyErr_Occurred()) {
PyErr_Clear();

View File

@@ -138,6 +138,9 @@ struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::
static constexpr bool rowMajor = Type::Flags & Eigen::RowMajorBit;
bool load(handle src, bool) {
if (!src)
return false;
object obj(src, true);
object sparse_module = module::import("scipy.sparse");
object matrix_type = sparse_module.attr(

View File

@@ -92,11 +92,11 @@ protected:
typename detail::intrinsic_type<Return>::type>::type> cast_out;
/* Dispatch code which converts function arguments and performs the actual function call */
rec->impl = [](detail::function_record *rec, handle args, handle parent) -> handle {
rec->impl = [](detail::function_record *rec, handle args, handle kwargs, handle parent) -> handle {
cast_in args_converter;
/* Try to cast the function arguments into the C++ domain */
if (!args_converter.load(args, true))
if (!args_converter.load_args(args, kwargs, true))
return PYBIND11_TRY_NEXT_OVERLOAD;
/* Invoke call policy pre-call hook */
@@ -106,7 +106,7 @@ protected:
capture *cap = (capture *) (sizeof(capture) <= sizeof(rec->data)
? &rec->data : rec->data[0]);
/* Perform the functionc all */
/* Perform the functioncall */
handle result = cast_out::cast(args_converter.template call<Return>(cap->f),
rec->policy, parent);
@@ -125,6 +125,9 @@ protected:
/* Register the function with Python from generic (non-templated) code */
initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args));
if (cast_in::has_args) rec->has_args = true;
if (cast_in::has_kwargs) rec->has_kwargs = true;
}
/// Register a function call with Python (generic non-templated code goes here)
@@ -204,9 +207,12 @@ protected:
std::to_string(args) + " arguments, but " + std::to_string(rec->args.size()) +
" pybind11::arg entries were specified!");
rec->is_constructor = !strcmp(rec->name, "__init__");
rec->signature = strdup(signature.c_str());
rec->args.shrink_to_fit();
rec->is_constructor = !strcmp(rec->name, "__init__");
rec->has_args = false;
rec->has_kwargs = false;
rec->nargs = args;
#if PY_MAJOR_VERSION < 3
if (rec->sibling && PyMethod_Check(rec->sibling.ptr()))
@@ -325,15 +331,15 @@ protected:
*it = overloads;
/* Need to know how many arguments + keyword arguments there are to pick the right overload */
int nargs = (int) PyTuple_Size(args),
nkwargs = kwargs ? (int) PyDict_Size(kwargs) : 0;
size_t nargs = PyTuple_GET_SIZE(args),
nkwargs = kwargs ? PyDict_Size(kwargs) : 0;
handle parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr,
handle parent = nargs > 0 ? PyTuple_GET_ITEM(args, 0) : nullptr,
result = PYBIND11_TRY_NEXT_OVERLOAD;
try {
for (; it != nullptr; it = it->next) {
tuple args_(args, true);
int kwargs_consumed = 0;
size_t kwargs_consumed = 0;
/* For each overload:
1. If the required list of arguments is longer than the
@@ -342,10 +348,11 @@ protected:
2. Ensure that all keyword arguments were "consumed"
3. Call the function call dispatcher (function_record::impl)
*/
if (nargs < (int) it->args.size()) {
args_ = tuple(it->args.size());
for (int i = 0; i < nargs; ++i) {
size_t nargs_ = nargs;
if (nargs < it->args.size()) {
nargs_ = it->args.size();
args_ = tuple(nargs_);
for (size_t i = 0; i < nargs; ++i) {
handle item = PyTuple_GET_ITEM(args, i);
PyTuple_SET_ITEM(args_.ptr(), i, item.inc_ref().ptr());
}
@@ -368,15 +375,16 @@ protected:
if (value) {
PyTuple_SET_ITEM(args_.ptr(), index, value.inc_ref().ptr());
} else {
kwargs_consumed = -1; /* definite failure */
kwargs_consumed = (size_t) -1; /* definite failure */
break;
}
}
}
try {
if (kwargs_consumed == nkwargs)
result = it->impl(it, args_, parent);
if ((kwargs_consumed == nkwargs || it->has_kwargs) &&
(nargs_ == it->nargs || it->has_args))
result = it->impl(it, args_, kwargs, parent);
} catch (cast_error &) {
result = PYBIND11_TRY_NEXT_OVERLOAD;
}
@@ -420,7 +428,7 @@ protected:
if (overloads->is_constructor) {
/* When a constructor ran successfully, the corresponding
holder type (e.g. std::unique_ptr) must still be initialized. */
PyObject *inst = PyTuple_GetItem(args, 0);
PyObject *inst = PyTuple_GET_ITEM(args, 0);
auto tinfo = detail::get_type_info(Py_TYPE(inst));
tinfo->init_holder(inst, nullptr);
}

View File

@@ -16,12 +16,8 @@
NAMESPACE_BEGIN(pybind11)
/* A few forward declarations */
class object;
class str;
class object;
class dict;
class iterator;
namespace detail { class accessor; class args; class kwargs; }
class object; class str; class object; class dict; class iterator;
namespace detail { class accessor; class args_proxy; class kwargs_proxy; }
/// Holds a reference to a Python object (no reference counting)
class handle {
@@ -43,17 +39,17 @@ public:
inline detail::accessor attr(const char *key) const;
inline pybind11::str str() const;
template <typename T> T cast() const;
template <typename ... Args>
template <typename ... Args>
[[deprecated("call(...) was deprecated in favor of operator()(...)")]]
object call(Args&&... args) const;
template <typename ... Args> object operator()(Args&&... args) const;
inline object operator()(detail::args args) const;
inline object operator()(detail::args args, detail::kwargs kwargs) const;
inline object operator()(detail::args_proxy args) const;
inline object operator()(detail::args_proxy f_args, detail::kwargs_proxy kwargs) const;
operator bool() const { return m_ptr != nullptr; }
bool operator==(const handle &h) const { return m_ptr == h.m_ptr; }
bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; }
bool check() const { return m_ptr != nullptr; }
inline detail::args operator*() const;
inline detail::args_proxy operator*() const;
protected:
PyObject *m_ptr;
};
@@ -218,17 +214,6 @@ private:
ssize_t pos = 0;
};
class kwargs : public handle {
public:
kwargs(handle h) : handle(h) { }
};
class args : public handle {
public:
args(handle h) : handle(h) { }
kwargs operator*() const { return kwargs(*this); }
};
inline bool PyIterable_Check(PyObject *obj) {
PyObject *iter = PyObject_GetIter(obj);
if (iter) {
@@ -239,20 +224,32 @@ inline bool PyIterable_Check(PyObject *obj) {
return false;
}
}
inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); }
class kwargs_proxy : public handle {
public:
kwargs_proxy(handle h) : handle(h) { }
};
class args_proxy : public handle {
public:
args_proxy(handle h) : handle(h) { }
kwargs_proxy operator*() const { return kwargs_proxy(*this); }
};
NAMESPACE_END(detail)
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, CvtStmt) \
Name(const handle &h, bool borrowed) : Parent(h, borrowed) { CvtStmt; } \
Name(const object& o): Parent(o) { CvtStmt; } \
Name(object&& o) noexcept : Parent(std::move(o)) { CvtStmt; } \
Name& operator=(object&& o) noexcept { (void) object::operator=(std::move(o)); CvtStmt; return *this; } \
Name& operator=(const object& o) { return static_cast<Name&>(object::operator=(o)); CvtStmt; } \
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); }
public: \
Name(const handle &h, bool borrowed) : Parent(h, borrowed) { CvtStmt; } \
Name(const object& o): Parent(o) { CvtStmt; } \
Name(object&& o) noexcept : Parent(std::move(o)) { CvtStmt; } \
Name& operator=(object&& o) noexcept { (void) object::operator=(std::move(o)); CvtStmt; return *this; } \
Name& operator=(const object& o) { return static_cast<Name&>(object::operator=(o)); CvtStmt; } \
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); }
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, )
@@ -332,7 +329,7 @@ inline detail::accessor handle::attr(handle key) const { return detail::accessor
inline detail::accessor handle::attr(const char *key) const { return detail::accessor(ptr(), key, true); }
inline iterator handle::begin() const { return iterator(PyObject_GetIter(ptr()), false); }
inline iterator handle::end() const { return iterator(nullptr, false); }
inline detail::args handle::operator*() const { return detail::args(*this); }
inline detail::args_proxy handle::operator*() const { return detail::args_proxy(*this); }
class str : public object {
public:
@@ -520,6 +517,9 @@ public:
void append(const object &object) const { PyList_Append(m_ptr, object.ptr()); }
};
class args : public list { PYBIND11_OBJECT_DEFAULT(args, list, PyList_Check) };
class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) };
class set : public object {
public:
PYBIND11_OBJECT(set, object, PySet_Check)