Commit a576e6a8 authored by Wenzel Jakob's avatar Wenzel Jakob
Browse files

keyword argument support, removed last traces of std::function<> usage

parent 71867830
......@@ -10,6 +10,8 @@
#pragma once
#include <pybind/pybind.h>
#include <functional>
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
......@@ -132,6 +134,15 @@ public:
}
};
#define DECL_FMT(t, n) template<> struct npy_format_descriptor<t> { enum { value = array::API::n }; }
DECL_FMT(int8_t, NPY_BYTE); DECL_FMT(uint8_t, NPY_UBYTE); DECL_FMT(int16_t, NPY_SHORT);
DECL_FMT(uint16_t, NPY_USHORT); DECL_FMT(int32_t, NPY_INT); DECL_FMT(uint32_t, NPY_UINT);
DECL_FMT(int64_t, NPY_LONGLONG); DECL_FMT(uint64_t, NPY_ULONGLONG); DECL_FMT(float, NPY_FLOAT);
DECL_FMT(double, NPY_DOUBLE); DECL_FMT(bool, NPY_BOOL); DECL_FMT(std::complex<float>, NPY_CFLOAT);
DECL_FMT(std::complex<double>, NPY_CDOUBLE);
#undef DECL_FMT
NAMESPACE_BEGIN(detail)
PYBIND_TYPE_CASTER_PYTYPE(array)
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<int8_t>) PYBIND_TYPE_CASTER_PYTYPE(array_dtype<uint8_t>)
......@@ -142,24 +153,20 @@ PYBIND_TYPE_CASTER_PYTYPE(array_dtype<float>) PYBIND_TYPE_CASTER_PYTYPE(array_
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<std::complex<float>>)
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<std::complex<double>>)
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<bool>)
NAMESPACE_END(detail)
#define DECL_FMT(t, n) template<> struct npy_format_descriptor<t> { enum { value = array::API::n }; }
DECL_FMT(int8_t, NPY_BYTE); DECL_FMT(uint8_t, NPY_UBYTE); DECL_FMT(int16_t, NPY_SHORT);
DECL_FMT(uint16_t, NPY_USHORT); DECL_FMT(int32_t, NPY_INT); DECL_FMT(uint32_t, NPY_UINT);
DECL_FMT(int64_t, NPY_LONGLONG); DECL_FMT(uint64_t, NPY_ULONGLONG); DECL_FMT(float, NPY_FLOAT);
DECL_FMT(double, NPY_DOUBLE); DECL_FMT(bool, NPY_BOOL); DECL_FMT(std::complex<float>, NPY_CFLOAT);
DECL_FMT(std::complex<double>, NPY_CDOUBLE);
#undef DECL_FMT
template <typename Func, typename Return, typename... Args>
struct vectorize_helper {
typename std::remove_reference<Func>::type f;
vectorize_helper(const Func &f) : f(f) { }
template <typename func_type, typename return_type, typename... args_type, size_t... Index>
std::function<object(array_dtype<args_type>...)>
vectorize(func_type &&f, return_type (*) (args_type ...),
detail::index_sequence<Index...>) {
object operator()(array_dtype<Args>... args) {
return run(args..., typename make_index_sequence<sizeof...(Args)>::type());
}
return [f](array_dtype<args_type>... args) -> array {
template <size_t ... Index> object run(array_dtype<Args>&... args, index_sequence<Index...>) {
/* Request buffers from all parameters */
const size_t N = sizeof...(args_type);
const size_t N = sizeof...(Args);
std::array<buffer_info, N> buffers {{ args.request()... }};
/* Determine dimensions parameters of output array */
......@@ -174,7 +181,7 @@ template <typename func_type, typename return_type, typename... args_type, size_
}
std::vector<size_t> strides(ndim);
if (ndim > 0) {
strides[ndim-1] = sizeof(return_type);
strides[ndim-1] = sizeof(Return);
for (int i=ndim-1; i>0; --i)
strides[i-1] = strides[i] * shape[i];
}
......@@ -186,31 +193,32 @@ template <typename func_type, typename return_type, typename... args_type, size_
}
/* Call the function */
std::vector<return_type> result(count);
std::vector<Return> result(count);
for (size_t i=0; i<count; ++i)
result[i] = f((buffers[Index].count == 1
? *((args_type *) buffers[Index].ptr)
: ((args_type *) buffers[Index].ptr)[i])...);
? *((Args *) buffers[Index].ptr)
: ((Args *) buffers[Index].ptr)[i])...);
if (count == 1)
return cast(result[0]);
/* Return the result */
return array(buffer_info(result.data(), sizeof(return_type),
format_descriptor<return_type>::value(),
return array(buffer_info(result.data(), sizeof(Return),
format_descriptor<Return>::value(),
ndim, shape, strides));
};
}
}
};
NAMESPACE_END(detail)
template <typename func_type, typename return_type, typename... args_type>
std::function<object(array_dtype<args_type>...)>
vectorize(func_type &&f, return_type (*f_) (args_type ...) = nullptr) {
return vectorize(f, f_, typename detail::make_index_sequence<sizeof...(args_type)>::type());
template <typename Func, typename Return, typename... Args>
detail::vectorize_helper<Func, Return, Args...> vectorize(const Func &f, Return (*) (Args ...)) {
return detail::vectorize_helper<Func, Return, Args...>(f);
}
template <typename return_type, typename... args_type>
std::function<object(array_dtype<args_type>...)> vectorize(return_type (*f) (args_type ...)) {
return vectorize(f, f);
template <typename Return, typename... Args>
detail::vectorize_helper<Return (*) (Args ...), Return, Args...> vectorize(Return (*f) (Args ...)) {
return vectorize<Return (*) (Args ...), Return, Args...>(f, f);
}
template <typename func> auto vectorize(func &&f) -> decltype(
......
......@@ -76,26 +76,82 @@ private:
template <typename... T> using arg_value_caster =
detail::type_caster<typename std::tuple<T...>>;
template <typename... T> void process_args(const std::tuple<T...> &args, function_entry *entry) {
process_args(args, entry, typename detail::make_index_sequence<sizeof...(T)>::type());
template <typename... T> static void process_extras(const std::tuple<T...> &args,
function_entry *entry, const char **kw, const char **def) {
process_extras(args, entry, kw, def, typename detail::make_index_sequence<sizeof...(T)>::type());
}
template <typename... T, size_t ... Index> void process_args(const
std::tuple<T...> &args, function_entry *entry,
detail::index_sequence<Index...>) {
int unused[] = { 0, (process_arg(std::get<Index>(args), entry), 0)... };
template <typename... T, size_t ... Index> static void process_extras(const std::tuple<T...> &args,
function_entry *entry, const char **kw, const char **def, detail::index_sequence<Index...>) {
int unused[] = { 0, (process_extra(std::get<Index>(args), entry, kw, def), 0)... };
(void) unused;
}
void process_arg(const char *doc, function_entry *entry) { entry->doc = doc; }
void process_arg(const pybind::doc &d, function_entry *entry) { entry->doc = d.value; }
void process_arg(const pybind::name &n, function_entry *entry) { entry->name = n.value; }
void process_arg(const pybind::arg &, function_entry *entry) { entry->keywords++; }
void process_arg(const pybind::is_method &, function_entry *entry) { entry->is_method = true; }
void process_arg(const pybind::return_value_policy p, function_entry *entry) { entry->policy = p; }
void process_arg(pybind::sibling s, function_entry *entry) { entry->sibling = s.value; }
template <typename... T> static void process_extras(const std::tuple<T...> &args,
PyObject *pyArgs, PyObject *kwargs, bool is_method) {
process_extras(args, pyArgs, kwargs, is_method, typename detail::make_index_sequence<sizeof...(T)>::type());
}
template <typename... T, size_t... Index> static void process_extras(const std::tuple<T...> &args,
PyObject *pyArgs, PyObject *kwargs, bool is_method, detail::index_sequence<Index...>) {
int index = is_method ? 1 : 0;
int unused[] = { 0, (process_extra(std::get<Index>(args), index, pyArgs, kwargs), 0)... };
(void) unused;
}
static void process_extra(const char *doc, function_entry *entry, const char **, const char **) { entry->doc = doc; }
static void process_extra(const pybind::doc &d, function_entry *entry, const char **, const char **) { entry->doc = d.value; }
static void process_extra(const pybind::name &n, function_entry *entry, const char **, const char **) { entry->name = n.value; }
static void process_extra(const pybind::arg &a, function_entry *entry, const char **kw, const char **) {
if (entry->is_method && entry->keywords == 0)
kw[entry->keywords++] = "self";
kw[entry->keywords++] = a.name;
}
template <typename T>
static void process_extra(const pybind::arg_t<T> &a, function_entry *entry, const char **kw, const char **def) {
if (entry->is_method && entry->keywords == 0)
kw[entry->keywords++] = "self";
kw[entry->keywords] = a.name;
def[entry->keywords++] = strdup(std::to_string(a.value).c_str());
}
static void process_extra(const pybind::is_method &, function_entry *entry, const char **, const char **) { entry->is_method = true; }
static void process_extra(const pybind::return_value_policy p, function_entry *entry, const char **, const char **) { entry->policy = p; }
static void process_extra(pybind::sibling s, function_entry *entry, const char **, const char **) { entry->sibling = s.value; }
template <typename T> static void process_extra(T, int &, PyObject *, PyObject *) { }
static void process_extra(const pybind::arg &a, int &index, PyObject *args, PyObject *kwargs) {
if (kwargs) {
if (PyTuple_GET_ITEM(args, index) != nullptr) {
index++;
return;
}
PyObject *value = PyDict_GetItemString(kwargs, a.name);
if (value) {
Py_INCREF(value);
PyTuple_SetItem(args, index, value);
}
}
index++;
}
template <typename T>
static void process_extra(const pybind::arg_t<T> &a, int &index, PyObject *args, PyObject *kwargs) {
if (PyTuple_GET_ITEM(args, index) != nullptr) {
index++;
return;
}
PyObject *value = nullptr;
if (kwargs)
value = PyDict_GetItemString(kwargs, a.name);
if (value) {
Py_INCREF(value);
} else {
value = detail::type_caster<typename detail::decay<T>::type>::cast(
a.value, return_value_policy::automatic, nullptr);
}
PyTuple_SetItem(args, index, value);
index++;
}
public:
cpp_function() { }
......@@ -104,7 +160,7 @@ public:
cpp_function(Return (*f)(Arg...), Extra&&... extra) {
struct capture {
Return (*f)(Arg...);
std::tuple<Extra...> extra;
std::tuple<Extra...> extras;
};
function_entry *entry = new function_entry();
......@@ -114,18 +170,23 @@ public:
typedef return_value_caster<Return> cast_out;
entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject * {
capture *data = (capture *) entry->data;
process_extras(data->extras, pyArgs, kwargs, entry->is_method);
cast_in args;
if (!args.load(pyArgs, true)) return nullptr;
auto f = ((capture *) entry->data)->f;
(void)kwargs;
return cast_out::cast(args.template call<Return>(f), entry->policy, parent);
if (!args.load(pyArgs, true))
return nullptr;
return cast_out::cast(args.template call<Return>(data->f), entry->policy, parent);
};
entry->signature = cast_in::name();
const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg);
std::array<const char *, N> kw{}, def{};
process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data());
entry->signature = cast_in::name(kw.data(), def.data());
entry->signature += " -> ";
entry->signature += cast_out::name();
process_args(((capture *) entry->data)->extra, entry);
initialize(entry);
initialize(entry, sizeof...(Arg));
}
/// Delegating helper constructor to deal with lambda functions
......@@ -136,7 +197,6 @@ public:
std::forward<Extra>(extra)...);
}
/// Class methods (non-const)
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
Return (Class::*f)(Arg...), Extra&&... extra) {
......@@ -157,7 +217,7 @@ private:
void initialize(Func &&f, Return (*)(Arg...), Extra&&... extra) {
struct capture {
typename std::remove_reference<Func>::type f;
std::tuple<Extra...> extra;
std::tuple<Extra...> extras;
};
function_entry *entry = new function_entry();
......@@ -167,27 +227,50 @@ private:
typedef return_value_caster<Return> cast_out;
entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject *{
capture *data = (capture *)entry->data;
process_extras(data->extras, pyArgs, kwargs, entry->is_method);
cast_in args;
if (!args.load(pyArgs, true)) return nullptr;
Func &f = ((capture *) entry->data)->f;
(void)kwargs;
return cast_out::cast(args.template call<Return>(f), entry->policy, parent);
if (!args.load(pyArgs, true))
return nullptr;
return cast_out::cast(args.template call<Return>(data->f), entry->policy, parent);
};
entry->signature = cast_in::name();
const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg);
std::array<const char *, N> kw{}, def{};
process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data());
entry->signature = cast_in::name(kw.data(), def.data());
entry->signature += " -> ";
entry->signature += cast_out::name();
process_args(((capture *) entry->data)->extra, entry);
initialize(entry);
initialize(entry, sizeof...(Arg));
}
static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs ) {
function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr);
int nargs = (int) PyTuple_Size(args);
PyObject *result = nullptr;
PyObject *parent = PyTuple_Size(args) > 0 ? PyTuple_GetItem(args, 0) : nullptr;
PyObject *parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr;
try {
for (function_entry *it = overloads; it != nullptr; it = it->next) {
if ((result = it->impl(it, args, kwargs, parent)) != nullptr)
PyObject *args_ = args;
if (it->keywords != 0 && it->keywords != nargs) {
args_ = PyTuple_New(it->keywords);
for (int i=0; i<nargs; ++i) {
PyObject *item = PyTuple_GET_ITEM(args, i);
Py_INCREF(item);
PyTuple_SET_ITEM(args_, i, item);
}
}
result = it->impl(it, args_, kwargs, parent);
if (args_ != args) {
Py_DECREF(args_);
}
if (result != nullptr)
break;
}
} catch (const error_already_set &) { return nullptr;
......@@ -221,9 +304,14 @@ private:
}
}
void initialize(function_entry *entry) {
void initialize(function_entry *entry, int args) {
if (entry->name == nullptr)
entry->name = "";
if (entry->keywords != 0 && entry->keywords != args)
throw std::runtime_error(
"cpp_function(): function \"" + std::string(entry->name) + "\" takes " +
std::to_string(args) + " arguments, but " + std::to_string(entry->keywords) +
" pybind::arg entries were specified!");
entry->is_constructor = !strcmp(entry->name, "__init__");
......@@ -578,32 +666,32 @@ public:
return *this;
}
class_ &def_property_readonly(const char *name, const cpp_function &fget) {
def_property(name, fget, cpp_function());
class_ &def_property_readonly(const char *name, const cpp_function &fget, const char *doc = nullptr) {
def_property(name, fget, cpp_function(), doc);
return *this;
}
class_ &def_property_readonly_static(const char *name, const cpp_function &fget) {
def_property_static(name, fget, cpp_function());
class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const char *doc = nullptr) {
def_property_static(name, fget, cpp_function(), doc);
return *this;
}
class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset) {
class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
object doc_obj = doc ? pybind::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
object property(
PyObject_CallFunction((PyObject *)&PyProperty_Type,
const_cast<char *>("OOOO"), fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None,
((object) const_cast<cpp_function&>(fget).attr("__doc__")).ptr()), false);
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr()), false);
attr(name) = property;
return *this;
}
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset) {
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
object doc_obj = doc ? pybind::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
object property(
PyObject_CallFunction((PyObject *)&PyProperty_Type,
const_cast<char *>("OOOO"), fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None,
((object) const_cast<cpp_function&>(fget).attr("__doc__")).ptr()), false);
const_cast<char *>("OOOs"), fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr()), false);
metaclass().attr(name) = property;
return *this;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment