From 71867830f531aeefe2a095773fd4f0ef977b9f43 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 29 Jul 2015 17:43:52 +0200 Subject: [PATCH] switched cpp_function to use variadic arguments --- include/pybind/operators.h | 20 +- include/pybind/pybind.h | 380 ++++++++++++++++++++----------------- 2 files changed, 218 insertions(+), 182 deletions(-) diff --git a/include/pybind/operators.h b/include/pybind/operators.h index c2c13383..e0f858ff 100644 --- a/include/pybind/operators.h +++ b/include/pybind/operators.h @@ -45,17 +45,17 @@ template struct op_impl { } /// Operator implementation generator template struct op_ { - template void execute(pybind::class_ &class_, const char *doc, return_value_policy policy) const { - typedef typename std::conditional::value, base, L>::type L_type; - typedef typename std::conditional::value, base, R>::type R_type; - typedef op_impl op; - class_.def(op::name(), &op::execute, doc, policy); + template void execute(pybind::class_ &class_, Extra&&... extra) const { + typedef typename std::conditional::value, Base, L>::type L_type; + typedef typename std::conditional::value, Base, R>::type R_type; + typedef op_impl op; + class_.def(op::name(), &op::execute, std::forward(extra)...); } - template void execute_cast(pybind::class_ &class_, const char *doc, return_value_policy policy) const { - typedef typename std::conditional::value, base, L>::type L_type; - typedef typename std::conditional::value, base, R>::type R_type; - typedef op_impl op; - class_.def(op::name(), &op::execute_cast, doc, policy); + template void execute_cast(pybind::class_ &class_, Extra&&... extra) const { + typedef typename std::conditional::value, Base, L>::type L_type; + typedef typename std::conditional::value, Base, R>::type R_type; + typedef op_impl op; + class_.def(op::name(), &op::execute_cast, std::forward(extra)...); } }; diff --git a/include/pybind/pybind.h b/include/pybind/pybind.h index 570d1939..4cfaab38 100644 --- a/include/pybind/pybind.h +++ b/include/pybind/pybind.h @@ -22,16 +22,48 @@ NAMESPACE_BEGIN(pybind) +template struct arg_t; + +/// Annotation for keyword arguments +struct arg { + arg(const char *name) : name(name) { } + template inline arg_t operator=(const T &value); + const char *name; +}; + +/// Annotation for keyword arguments with default values +template struct arg_t : public arg { + arg_t(const char *name, const T &value) : arg(name), value(value) { } + T value; +}; +template inline arg_t arg::operator=(const T &value) { return arg_t(name, value); } + +/// Annotation for methods +struct is_method { }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation for function siblings +struct sibling { PyObject *value; sibling(handle value) : value(value.ptr()) { } }; + /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object class cpp_function : public function { private: /// Chained list of function entries for overloading struct function_entry { - PyObject * (*impl) (function_entry *, PyObject *, PyObject *); + const char *name = nullptr; + PyObject * (*impl) (function_entry *, PyObject *, PyObject *, PyObject *); void *data; - std::string signature, doc; - bool is_constructor; - return_value_policy policy; + bool is_constructor = false, is_method = false; + short keywords = 0; + return_value_policy policy = return_value_policy::automatic; + std::string signature; + PyObject *sibling = nullptr; + const char *doc = nullptr; function_entry *next = nullptr; }; @@ -41,93 +73,121 @@ private: std::is_void::value, detail::void_type, typename detail::decay::type>::type>; /// Picks a suitable argument value converter from cast.h - template using arg_value_caster = + template using arg_value_caster = detail::type_caster>; + + + template void process_args(const std::tuple &args, function_entry *entry) { + process_args(args, entry, typename detail::make_index_sequence::type()); + } + + template void process_args(const + std::tuple &args, function_entry *entry, + detail::index_sequence) { + int unused[] = { 0, (process_arg(std::get(args), entry), 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; } + public: cpp_function() { } /// Vanilla function pointers - template - cpp_function(return_type (*f)(arg_type...), const char *name = nullptr, - const char *doc = nullptr, return_value_policy policy = return_value_policy::automatic, - const function &sibling = function(), bool is_method = false) { - - typedef arg_value_caster cast_in; - typedef return_value_caster cast_out; - - auto impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject * { - cast_in args; - if (!args.load(pyArgs, true)) return nullptr; - auto f = (return_type (*) (arg_type...)) entry->data; - return cast_out::cast(args.template call(f), - entry->policy, parent); + template + cpp_function(Return (*f)(Arg...), Extra&&... extra) { + struct capture { + Return (*f)(Arg...); + std::tuple extra; }; - initialize(name, doc, cast_in::name() + std::string(" -> ") + cast_out::name(), - sibling, is_method, policy, impl, (void *) f); + function_entry *entry = new function_entry(); + entry->data = new capture { f, std::tuple(std::forward(extra)...) }; + + typedef arg_value_caster cast_in; + typedef return_value_caster cast_out; + + entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject * { + 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(f), entry->policy, parent); + }; + + entry->signature = cast_in::name(); + entry->signature += " -> "; + entry->signature += cast_out::name(); + process_args(((capture *) entry->data)->extra, entry); + initialize(entry); } /// Delegating helper constructor to deal with lambda functions - template - cpp_function(func &&f, const char *name = nullptr, - const char *doc = nullptr, - return_value_policy policy = return_value_policy::automatic, - const function &sibling = function(), bool is_method = false) { - initialize(std::forward(f), name, doc, policy, sibling, is_method, + template cpp_function(Func &&f, Extra&&... extra) { + initialize(std::forward(f), (typename detail::remove_class::type::operator())>::type *) nullptr); + &std::remove_reference::type::operator())>::type *) nullptr, + std::forward(extra)...); } /// Class methods (non-const) - template cpp_function( - return_type (class_type::*f)(arg_type...), const char *name = nullptr, - const char *doc = nullptr, return_value_policy policy = return_value_policy::automatic, - const function &sibling = function(), bool is_method = false) { - initialize([f](class_type *c, arg_type... args) -> return_type { return (c->*f)(args...); }, - name, doc, policy, sibling, is_method, (return_type (*)(class_type *, arg_type ...)) nullptr); + template cpp_function( + Return (Class::*f)(Arg...), Extra&&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, std::forward(extra)...); } /// Class methods (const) - template cpp_function( - return_type (class_type::*f)(arg_type...) const, const char *name = nullptr, - const char *doc = nullptr, return_value_policy policy = return_value_policy::automatic, - const function &sibling = function(), bool is_method = false) { - initialize([f](const class_type *c, arg_type... args) -> return_type { return (c->*f)(args...); }, - name, doc, policy, sibling, is_method, (return_type (*)(const class_type *, arg_type ...)) nullptr); + template cpp_function( + Return (Class::*f)(Arg...) const, Extra&&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, std::forward(extra)...); } private: /// Functors, lambda functions, etc. - template - void initialize(func &&f, const char *name, const char *doc, - return_value_policy policy, const function &sibling, - bool is_method, return_type (*)(arg_type...)) { - - typedef arg_value_caster cast_in; - typedef return_value_caster cast_out; - struct capture { typename std::remove_reference::type f; }; - void *ptr = new capture { std::forward(f) }; - - auto impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject *{ - cast_in args; - if (!args.load(pyArgs, true)) return nullptr; - func &f = ((capture *) entry->data)->f; - return cast_out::cast(args.template call(f), - entry->policy, parent); + template + void initialize(Func &&f, Return (*)(Arg...), Extra&&... extra) { + struct capture { + typename std::remove_reference::type f; + std::tuple extra; }; - initialize(name, doc, cast_in::name() + std::string(" -> ") + cast_out::name(), - sibling, is_method, policy, impl, ptr); + function_entry *entry = new function_entry(); + entry->data = new capture { std::forward(f), std::tuple(std::forward(extra)...) }; + + typedef arg_value_caster cast_in; + typedef return_value_caster cast_out; + + entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject *{ + 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(f), entry->policy, parent); + }; + + entry->signature = cast_in::name(); + entry->signature += " -> "; + entry->signature += cast_out::name(); + process_args(((capture *) entry->data)->extra, entry); + initialize(entry); } - static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject * /* kwargs */) { + static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs ) { function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr); PyObject *result = nullptr; PyObject *parent = PyTuple_Size(args) > 0 ? PyTuple_GetItem(args, 0) : nullptr; try { for (function_entry *it = overloads; it != nullptr; it = it->next) { - if ((result = it->impl(it, args, parent)) != nullptr) + if ((result = it->impl(it, args, kwargs, parent)) != nullptr) break; } } catch (const error_already_set &) { return nullptr; @@ -161,27 +221,16 @@ private: } } - void initialize(const char *name, const char *doc, - const std::string &signature, function sibling, - bool is_method, return_value_policy policy, - PyObject *(*impl) (function_entry *, PyObject *, PyObject *), - void *data) { - if (name == nullptr) - name = ""; + void initialize(function_entry *entry) { + if (entry->name == nullptr) + entry->name = ""; - /* Linked list of function call handlers (for overloading) */ - function_entry *entry = new function_entry(); - entry->impl = impl; - entry->is_constructor = !strcmp(name, "__init__"); - entry->policy = policy; - entry->signature = signature; - entry->data = data; - if (doc) entry->doc = doc; + entry->is_constructor = !strcmp(entry->name, "__init__"); - if (!sibling.ptr() || !PyCFunction_Check(sibling.ptr())) { + if (!entry->sibling || !PyCFunction_Check(entry->sibling)) { PyMethodDef *def = new PyMethodDef(); memset(def, 0, sizeof(PyMethodDef)); - def->ml_name = name != nullptr ? strdup(name) : name; + def->ml_name = entry->name; def->ml_meth = reinterpret_cast(*dispatcher); def->ml_flags = METH_VARARGS | METH_KEYWORDS; capsule entry_capsule(entry); @@ -189,7 +238,7 @@ private: if (!m_ptr) throw std::runtime_error("cpp_function::cpp_function(): Could not allocate function object"); } else { - m_ptr = sibling.ptr(); + m_ptr = entry->sibling; inc_ref(); capsule entry_capsule(PyCFunction_GetSelf(m_ptr), true); function_entry *parent = (function_entry *) entry_capsule, *backup = parent; @@ -199,22 +248,23 @@ private: entry = backup; } std::string signatures; - int it = 0; - while (entry) { /* Create pydoc entry */ - if (sibling.ptr()) - signatures += std::to_string(++it) + ". "; - signatures += "Signature : " + std::string(entry->signature) + "\n"; - if (!entry->doc.empty()) - signatures += "\n" + std::string(entry->doc) + "\n"; - if (entry->next) + int index = 0; + function_entry *it = entry; + while (it) { /* Create pydoc it */ + if (it->sibling) + signatures += std::to_string(++index) + ". "; + signatures += "Signature : " + std::string(it->signature) + "\n"; + if (it->doc && strlen(it->doc) > 0) + signatures += "\n" + std::string(it->doc) + "\n"; + if (it->next) signatures += "\n"; - entry = entry->next; + it = it->next; } PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; if (func->m_ml->ml_doc) std::free((char *) func->m_ml->ml_doc); func->m_ml->ml_doc = strdup(signatures.c_str()); - if (is_method) { + if (entry->is_method) { m_ptr = PyInstanceMethod_New(m_ptr); if (!m_ptr) throw std::runtime_error("cpp_function::cpp_function(): Could not allocate instance method object"); @@ -223,15 +273,6 @@ private: } }; -class cpp_method : public cpp_function { -public: - cpp_method () { } - template cpp_method(func &&f, const char *name = nullptr, - const char *doc = nullptr, return_value_policy - policy = return_value_policy::automatic, function sibling = function()) - : cpp_function(std::forward(f), name, doc, policy, sibling, true) {} -}; - class module : public object { public: PYBIND_OBJECT_DEFAULT(module, object, PyModule_Check) @@ -249,12 +290,12 @@ public: inc_ref(); } - template - module &def(const char *name, Func f, const char *doc = nullptr, - return_value_policy policy = return_value_policy::automatic) { - cpp_function func(f, name, doc, policy, (function) attr(name)); + template + module &def(const char *name_, Func &&f, Extra&& ... extra) { + cpp_function func(std::forward(f), name(name_), + sibling((handle) attr(name_)), std::forward(extra)...); func.inc_ref(); /* The following line steals a reference to 'func' */ - PyModule_AddObject(ptr(), name, func.ptr()); + PyModule_AddObject(ptr(), name_, func.ptr()); return *this; } @@ -270,13 +311,6 @@ public: }; NAMESPACE_BEGIN(detail) -/* Forward declarations */ -enum op_id : int; -enum op_type : int; -struct undefined_t; -template struct op_; -template struct init; - /// Basic support for creating new Python heap types class custom_type : public object { public: @@ -435,6 +469,13 @@ protected: static void releasebuffer(PyObject *, Py_buffer *view) { delete (buffer_info *) view->internal; } }; + +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +template struct init; NAMESPACE_END(detail) template > class class_ : public detail::custom_type { @@ -454,42 +495,41 @@ public: sizeof(instance_type), init_holder, dealloc, parent.ptr(), doc) { } - template - class_ &def(const char *name, Func f, const char *doc = nullptr, - return_value_policy policy = return_value_policy::automatic) { - attr(name) = cpp_method(f, name, doc, policy, (function) attr(name)); + template + class_ &def(const char *name_, Func&& f, Extra&&... extra) { + attr(name_) = cpp_function(std::forward(f), name(name_), + sibling(attr(name_)), is_method(), + std::forward(extra)...); return *this; } - template class_ & - def_static(const char *name, Func f, const char *doc = nullptr, - return_value_policy policy = return_value_policy::automatic) { - attr(name) = cpp_function(f, name, doc, policy, (function) attr(name)); + template class_ & + def_static(const char *name_, Func f, Extra&&... extra) { + attr(name_) = cpp_function(std::forward(f), name(name_), + sibling(attr(name_)), + std::forward(extra)...); return *this; } - template - class_ &def(const detail::op_ &op, const char *doc = nullptr, - return_value_policy policy = return_value_policy::automatic) { - op.template execute(*this, doc, policy); + template + class_ &def(const detail::op_ &op, Extra&&... extra) { + op.template execute(*this, std::forward(extra)...); return *this; } - template class_ & - def_cast(const detail::op_ &op, const char *doc = nullptr, - return_value_policy policy = return_value_policy::automatic) { - op.template execute_cast(*this, doc, policy); + template + class_ & def_cast(const detail::op_ &op, Extra&&... extra) { + op.template execute_cast(*this, std::forward(extra)...); return *this; } - template - class_ &def(const detail::init &init, const char *doc = nullptr) { - init.template execute(*this, doc); + template + class_ &def(const detail::init &init, Extra&&... extra) { + init.template execute(*this, std::forward(extra)...); return *this; } - template - class_& def_buffer(Func &&func) { + template class_& def_buffer(Func &&func) { struct capture { Func func; }; capture *ptr = new capture { std::forward(func) }; install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { @@ -501,73 +541,69 @@ public: return *this; } - template - class_ &def_readwrite(const char *name, D C::*pm, - const char *doc = nullptr) { - cpp_method fget([pm](const C &c) -> const D &{ return c.*pm; }, nullptr, - nullptr, return_value_policy::reference_internal), - fset([pm](C &c, const D &value) { c.*pm = value; }); - def_property(name, fget, fset, doc); + template + class_ &def_readwrite(const char *name, D C::*pm, Extra&&... extra) { + cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, + return_value_policy::reference_internal, + is_method(), extra...), + fset([pm](C &c, const D &value) { c.*pm = value; }, + is_method(), extra...); + def_property(name, fget, fset); return *this; } - template - class_ &def_readonly(const char *name, const D C::*pm, - const char *doc = nullptr) { - cpp_method fget([pm](const C &c) -> const D &{ return c.*pm; }, nullptr, - nullptr, return_value_policy::reference_internal); - def_property(name, fget, doc); + template + class_ &def_readonly(const char *name, const D C::*pm, Extra&& ...extra) { + cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, + return_value_policy::reference_internal, + is_method(), std::forward(extra)...); + def_property_readonly(name, fget); return *this; } - template - class_ &def_readwrite_static(const char *name, D *pm, - const char *doc = nullptr) { + template + class_ &def_readwrite_static(const char *name, D *pm, Extra&& ...extra) { cpp_function fget([pm](object) -> const D &{ return *pm; }, nullptr, - nullptr, return_value_policy::reference_internal), - fset([pm](object, const D &value) { *pm = value; }); - def_property_static(name, fget, fset, doc); + return_value_policy::reference_internal, extra...), + fset([pm](object, const D &value) { *pm = value; }, extra...); + def_property_static(name, fget, fset); return *this; } - template - class_ &def_readonly_static(const char *name, const D *pm, - const char *doc = nullptr) { + template + class_ &def_readonly_static(const char *name, const D *pm, Extra&& ...extra) { cpp_function fget([pm](object) -> const D &{ return *pm; }, nullptr, - nullptr, return_value_policy::reference_internal); - def_property_static(name, fget, doc); + return_value_policy::reference_internal, std::forward(extra)...); + def_property_readonly_static(name, fget); return *this; } - class_ &def_property(const char *name, const cpp_method &fget, - const char *doc = nullptr) { - def_property(name, fget, cpp_method(), doc); + class_ &def_property_readonly(const char *name, const cpp_function &fget) { + def_property(name, fget, cpp_function()); return *this; } - class_ &def_property_static(const char *name, const cpp_function &fget, - const char *doc = nullptr) { - def_property_static(name, fget, cpp_function(), doc); + class_ &def_property_readonly_static(const char *name, const cpp_function &fget) { + def_property_static(name, fget, cpp_function()); return *this; } - class_ &def_property(const char *name, const cpp_method &fget, - const cpp_method &fset, const char *doc = nullptr) { + class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset) { object property( PyObject_CallFunction((PyObject *)&PyProperty_Type, - const_cast("OOOs"), fget.ptr() ? fget.ptr() : Py_None, - fset.ptr() ? fset.ptr() : Py_None, Py_None, doc), false); + const_cast("OOOO"), fget.ptr() ? fget.ptr() : Py_None, + fset.ptr() ? fset.ptr() : Py_None, Py_None, + ((object) const_cast(fget).attr("__doc__")).ptr()), false); attr(name) = property; return *this; } - class_ &def_property_static(const char *name, const cpp_function &fget, - const cpp_function &fset, - const char *doc = nullptr) { + class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset) { object property( PyObject_CallFunction((PyObject *)&PyProperty_Type, - const_cast("OOOs"), fget.ptr() ? fget.ptr() : Py_None, - fset.ptr() ? fset.ptr() : Py_None, Py_None, doc), false); + const_cast("OOOO"), fget.ptr() ? fget.ptr() : Py_None, + fset.ptr() ? fset.ptr() : Py_None, Py_None, + ((object) const_cast(fget).attr("__doc__")).ptr()), false); metaclass().attr(name) = property; return *this; } @@ -626,10 +662,10 @@ private: }; NAMESPACE_BEGIN(detail) -template struct init { - template void execute(pybind::class_ &class_, const char *doc) const { +template struct init { + template void execute(pybind::class_ &class_, Extra&&... extra) const { /// Function which calls a specific C++ in-place constructor - class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, doc); + class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, std::forward(extra)...); } }; NAMESPACE_END(detail)