From bd4a529319c45d9a87b8b5c60155bcc06d7a742f Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 11 Jul 2015 17:41:48 +0200 Subject: [PATCH] more flexible function creation syntax --- .gitignore | 1 + include/pybind/cast.h | 14 ++--- include/pybind/common.h | 5 +- include/pybind/mpl.h | 17 +++-- include/pybind/operators.h | 7 +-- include/pybind/pybind.h | 126 +++++++++++++++++++++---------------- include/pybind/pytypes.h | 14 +++-- include/pybind/typeid.h | 8 +-- 8 files changed, 103 insertions(+), 89 deletions(-) diff --git a/.gitignore b/.gitignore index ed5902cc..88368db0 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ Debug .vs CTestTestfile.cmake Testing +autogen diff --git a/include/pybind/cast.h b/include/pybind/cast.h index 6b3b96e3..63ed19c9 100644 --- a/include/pybind/cast.h +++ b/include/pybind/cast.h @@ -8,12 +8,11 @@ BSD-style license that can be found in the LICENSE file. */ -#if !defined(__PYBIND_CAST) -#define __PYBIND_CAST +#pragma once -#include "pytypes.h" -#include "mpl.h" -#include "typeid.h" +#include +#include +#include #include #include @@ -474,10 +473,11 @@ TYPE_CASTER_PYTYPE(int_) TYPE_CASTER_PYTYPE(list) TYPE_CASTER_PYTYPE(slice) TYPE_CASTER_PYTYPE(tuple) +TYPE_CASTER_PYTYPE(function) #undef TYPE_CASTER -#undef TYPE_CASTER_NUMBER #undef TYPE_CASTER_PYTYPE +#undef TYPE_CASTER_NUMBER NAMESPACE_END(detail) @@ -522,5 +522,3 @@ template inline object handle::call(Args&&... args_) { } NAMESPACE_END(pybind) - -#endif /* __PYBIND_CAST */ diff --git a/include/pybind/common.h b/include/pybind/common.h index 8310096c..0bbaa413 100644 --- a/include/pybind/common.h +++ b/include/pybind/common.h @@ -7,8 +7,7 @@ BSD-style license that can be found in the LICENSE file. */ -#if !defined(__PYBIND_COMMON_H) -#define __PYBIND_COMMON_H +#pragma once #if !defined(NAMESPACE_BEGIN) #define NAMESPACE_BEGIN(name) namespace name { @@ -145,5 +144,3 @@ inline internals &get_internals(); NAMESPACE_END(detail) NAMESPACE_END(pybind) - -#endif /* __PYBIND_COMMON_H */ diff --git a/include/pybind/mpl.h b/include/pybind/mpl.h index ff9c6ebf..27d18b40 100644 --- a/include/pybind/mpl.h +++ b/include/pybind/mpl.h @@ -7,10 +7,9 @@ BSD-style license that can be found in the LICENSE file. */ -#if !defined(__PYBIND_MPL_H) -#define __PYBIND_MPL_H +#pragma once -#include "common.h" +#include #include NAMESPACE_BEGIN(pybind) @@ -80,7 +79,15 @@ template <> struct tuple_dispatch { NAMESPACE_END(detail) /// For lambda functions delegate to their 'operator()' -template struct function_traits : public function_traits> { }; +template struct function_traits : function_traits> { }; + +/* Deal with reference arguments */ +template + struct function_traits : function_traits {}; +template + struct function_traits : function_traits {}; +template + struct function_traits : function_traits {}; /// Type traits for function pointers template @@ -186,5 +193,3 @@ struct function_traits> { NAMESPACE_END(mpl) NAMESPACE_END(pybind) - -#endif /* __PYBIND_MPL_H */ diff --git a/include/pybind/operators.h b/include/pybind/operators.h index 08fcd991..9e3011a2 100644 --- a/include/pybind/operators.h +++ b/include/pybind/operators.h @@ -7,10 +7,9 @@ BSD-style license that can be found in the LICENSE file. */ -#if !defined(__PYBIND_OPERATOR) -#define __PYBIND_OPERATOR +#pragma once -#include "pybind.h" +#include #include NAMESPACE_BEGIN(pybind) @@ -147,5 +146,3 @@ NAMESPACE_END(detail) using detail::self; NAMESPACE_END(pybind) - -#endif /* __PYBIND_OPERATOR */ diff --git a/include/pybind/pybind.h b/include/pybind/pybind.h index ad92a824..9f285616 100644 --- a/include/pybind/pybind.h +++ b/include/pybind/pybind.h @@ -7,8 +7,7 @@ BSD-style license that can be found in the LICENSE file. */ -#if !defined(__PYBIND_H) -#define __PYBIND_H +#pragma once #if defined(_MSC_VER) #pragma warning(push) @@ -19,32 +18,31 @@ #pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted #endif -#include "cast.h" +#include NAMESPACE_BEGIN(pybind) -class function : public object { -private: +class cpp_function : public function { +public: struct function_entry { std::function impl; std::string signature, doc; bool is_constructor; function_entry *next = nullptr; }; -public: - PYTHON_OBJECT_DEFAULT(function, object, PyFunction_Check) - template - function(const char *name, Func _func, bool is_method, - function overload_sibling = function(), const char *doc = nullptr, - return_value_policy policy = return_value_policy::automatic) { + cpp_function() { } + template cpp_function( + Func &&_func, const char *name = nullptr, const char *doc = nullptr, + return_value_policy policy = return_value_policy::automatic, + function sibling = function(), bool is_method = false) { /* Function traits extracted from the template type 'Func' */ typedef mpl::function_traits f_traits; /* Suitable input and output casters */ typedef typename detail::type_caster cast_in; typedef typename detail::type_caster::type> cast_out; - typename f_traits::f_type func = f_traits::cast(_func); + typename f_traits::f_type func = f_traits::cast(std::forward(_func)); auto impl = [func, policy](PyObject *pyArgs) -> PyObject *{ cast_in args; @@ -53,20 +51,13 @@ public: PyObject *parent = policy != return_value_policy::reference_internal ? nullptr : PyTuple_GetItem(pyArgs, 0); return cast_out::cast( - f_traits::dispatch(func, (typename f_traits::args_type) args), + f_traits::dispatch(func, args.operator typename f_traits::args_type()), policy, parent); }; - /* Linked list of function call handlers (for overloading) */ - function_entry *entry = new function_entry(); - entry->impl = impl; - entry->signature = std::string(name) + cast_in::name() + std::string(" -> ") + cast_out::name(); - entry->is_constructor = !strcmp(name, "__init__"); - if (doc) entry->doc = doc; - - install_function(name, entry, is_method, overload_sibling); + initialize(name, doc, cast_in::name() + std::string(" -> ") + cast_out::name(), + sibling, is_method, std::move(impl)); } - private: static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject * /* kwargs */) { function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr); @@ -107,19 +98,31 @@ private: } } - void install_function(const char *name, function_entry *entry, bool is_method, function overload_sibling) { - if (!overload_sibling.ptr() || !PyCFunction_Check(overload_sibling.ptr())) { + void initialize(const char *name, const char *doc, + const std::string &signature, function sibling, + bool is_method, std::function &&impl) { + if (name == nullptr) + name = ""; + + /* Linked list of function call handlers (for overloading) */ + function_entry *entry = new function_entry(); + entry->impl = std::move(impl); + entry->is_constructor = !strcmp(name, "__init__"); + entry->signature = signature; + if (doc) entry->doc = doc; + + if (!sibling.ptr() || !PyCFunction_Check(sibling.ptr())) { PyMethodDef *def = new PyMethodDef(); memset(def, 0, sizeof(PyMethodDef)); - def->ml_name = strdup(name); + def->ml_name = name != nullptr ? strdup(name) : name; def->ml_meth = reinterpret_cast(*dispatcher); def->ml_flags = METH_VARARGS | METH_KEYWORDS; capsule entry_capsule(entry); m_ptr = PyCFunction_New(def, entry_capsule.ptr()); if (!m_ptr) - throw std::runtime_error("function::function(): Could not allocate function object"); + throw std::runtime_error("cpp_function::cpp_function(): Could not allocate function object"); } else { - m_ptr = overload_sibling.ptr(); + m_ptr = sibling.ptr(); inc_ref(); capsule entry_capsule(PyCFunction_GetSelf(m_ptr), true); function_entry *parent = (function_entry *) entry_capsule, *backup = parent; @@ -144,12 +147,22 @@ private: if (is_method) { m_ptr = PyInstanceMethod_New(m_ptr); if (!m_ptr) - throw std::runtime_error("function::function(): Could not allocate instance method object"); + throw std::runtime_error("cpp_function::cpp_function(): Could not allocate instance method object"); Py_DECREF(func); } } }; +class cpp_method : public cpp_function { +public: + cpp_method () { } + template + cpp_method(Func &&_func, const char *name = nullptr, const char *doc = nullptr, + return_value_policy policy = return_value_policy::automatic, + function sibling = function()) + : cpp_function(std::forward(_func), name, doc, policy, sibling, true) { } +}; + class module : public object { public: PYTHON_OBJECT_DEFAULT(module, object, PyModule_Check) @@ -167,17 +180,21 @@ public: inc_ref(); } - template module& def(const char *name, Func f, const char *doc = nullptr) { - function func(name, f, false, (function) attr(name), doc); + 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)); func.inc_ref(); /* The following line steals a reference to 'func' */ PyModule_AddObject(ptr(), name, func.ptr()); return *this; } - module def_submodule(const char *name) { + module def_submodule(const char *name, const char *doc = nullptr) { std::string full_name = std::string(PyModule_GetName(m_ptr)) + std::string(".") + std::string(name); module result(PyImport_AddModule(full_name.c_str()), true); + if (doc) + result.attr("__doc__") = pybind::str(doc); attr(name) = result; return result; } @@ -218,7 +235,6 @@ public: type->ht_name = type->ht_qualname = name; type->ht_type.tp_name = strdup(full_name.c_str()); type->ht_type.tp_basicsize = instance_size; - type->ht_type.tp_doc = doc; type->ht_type.tp_init = (initproc) init; type->ht_type.tp_new = (newfunc) new_instance; type->ht_type.tp_dealloc = dealloc; @@ -244,6 +260,8 @@ public: type_info.type_size = type_size; type_info.init_holder = init_holder; attr("__pybind__") = capsule(&type_info); + if (doc) + attr("__doc__") = pybind::str(doc); scope.attr(name) = *this; } @@ -366,14 +384,14 @@ public: template class_ &def(const char *name, Func f, const char *doc = nullptr, return_value_policy policy = return_value_policy::automatic) { - attr(name) = function(name, f, true, (function) attr(name), doc, policy); + attr(name) = cpp_method(f, name, doc, policy, (function) attr(name)); 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) = function(name, f, false, (function) attr(name), doc, policy); + attr(name) = cpp_function(f, name, doc, policy, (function) attr(name)); return *this; } @@ -409,19 +427,19 @@ public: template class_ &def_readwrite(const char *name, D C::*pm, - const char *doc = nullptr) { - function fget("", [=](C * ptr) -> D & { return ptr->*pm; }, true, - function(), doc, return_value_policy::reference_internal), - fset("", [=](C *ptr, const D &value) { ptr->*pm = value; }, true, function(), doc); + 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); return *this; } template class_ &def_readonly(const char *name, const D C::*pm, - const char *doc = nullptr) { - function fget("", [=](C * ptr) -> const D & { return ptr->*pm; }, true, - function(), doc, return_value_policy::reference_internal); + 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); return *this; } @@ -429,8 +447,9 @@ public: template class_ &def_readwrite_static(const char *name, D *pm, const char *doc = nullptr) { - function fget("", [=](object) -> D & { return *pm; }, true), - fset("", [=](object, const D &value) { *pm = value; }, true); + 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 *this; } @@ -438,25 +457,26 @@ public: template class_ &def_readonly_static(const char *name, const D *pm, const char *doc = nullptr) { - function fget("", [=](object) -> const D & { return *pm; }, true); + cpp_function fget([pm](object) -> const D &{ return *pm; }, nullptr, + nullptr, return_value_policy::reference_internal); def_property_static(name, fget, doc); return *this; } - class_ &def_property(const char *name, const function &fget, + class_ &def_property(const char *name, const cpp_method &fget, const char *doc = nullptr) { - def_property(name, fget, function(), doc); + def_property(name, fget, cpp_method(), doc); return *this; } - class_ &def_property_static(const char *name, const function &fget, + class_ &def_property_static(const char *name, const cpp_function &fget, const char *doc = nullptr) { - def_property_static(name, fget, function(), doc); + def_property_static(name, fget, cpp_function(), doc); return *this; } - class_ &def_property(const char *name, const function &fget, - const function &fset, const char *doc = nullptr) { + class_ &def_property(const char *name, const cpp_method &fget, + const cpp_method &fset, const char *doc = nullptr) { object property( PyObject_CallFunction((PyObject *)&PyProperty_Type, const_cast("OOOs"), fget.ptr() ? fget.ptr() : Py_None, @@ -465,8 +485,8 @@ public: return *this; } - class_ &def_property_static(const char *name, const function &fget, - const function &fset, + class_ &def_property_static(const char *name, const cpp_function &fget, + const cpp_function &fset, const char *doc = nullptr) { object property( PyObject_CallFunction((PyObject *)&PyProperty_Type, @@ -583,5 +603,3 @@ NAMESPACE_END(pybind) #undef PYTHON_OBJECT #undef PYTHON_OBJECT_DEFAULT - -#endif /* __PYBIND_H */ diff --git a/include/pybind/pytypes.h b/include/pybind/pytypes.h index 3a7b1cd5..db612b59 100644 --- a/include/pybind/pytypes.h +++ b/include/pybind/pytypes.h @@ -7,10 +7,9 @@ BSD-style license that can be found in the LICENSE file. */ -#if !defined(__PYBIND_PYTYPES_H) -#define __PYBIND_PYTYPES_H +#pragma once -#include "common.h" +#include #include NAMESPACE_BEGIN(pybind) @@ -208,7 +207,7 @@ inline detail::accessor handle::attr(const char *key) { return detail::accessor( #define PYTHON_OBJECT_DEFAULT(Name, Parent, CheckFun) \ PYTHON_OBJECT(Name, Parent, CheckFun) \ - Name() : object() { } + Name() : Parent() { } class str : public object { public: @@ -295,6 +294,11 @@ public: void append(const object &object) { PyList_Append(m_ptr, (PyObject *) object.ptr()); } }; +class function : public object { +public: + PYTHON_OBJECT_DEFAULT(function, object, PyFunction_Check) +}; + class buffer : public object { public: PYTHON_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) @@ -335,5 +339,3 @@ inline internals &get_internals() { } NAMESPACE_END(detail) NAMESPACE_END(pybind) - -#endif /* __PYBIND_PYTYPES_H */ diff --git a/include/pybind/typeid.h b/include/pybind/typeid.h index ee0b023b..c452c3f5 100644 --- a/include/pybind/typeid.h +++ b/include/pybind/typeid.h @@ -7,10 +7,9 @@ BSD-style license that can be found in the LICENSE file. */ -#if !defined(__PYBIND_TYPEID_H) -#define __PYBIND_TYPEID_H +#pragma once -#include "common.h" +#include #include #include #if defined(__GNUG__) @@ -48,6 +47,3 @@ template static std::string type_id() { } NAMESPACE_END(pybind) - -#endif /* __PYBIND_TYPEID_H */ -