c++ - Boost.Python and Boost.Function -
i want wrap boost::function
class member can used in following way:
using namespace boost; using namespace boost::python; struct gui_button_t { function<void()> on_pressed; }; class_<gui_button_t>("guibutton", init<>()) .def("on_pressed", &gui_button_t::on_pressed);
and in python:
def callback_function(): print 'button has been pressed' button = guibutton() button.on_pressed = callback_function button.on_pressed() # function should callable c++ or python
however, trying yields enormous amount of errors regarding class template parameters , on.
i've done bit of searching haven't been able find answer i've been looking for. following article sort of come close don't touch directly on subject.
http://bfroehle.com/2011/07/18/boost-python-and-boost-function-ii/
what doing wrong here? need desired interface functionality?
many in advance.
boost.python accepts pointers functions , pointers member functions. need convert our callable function pointer. key ideas here
- a lambda has no capture can converted function pointer (via sorcery)
- function pointers interpreted same way member functions in python: first argument
self
so in case, need generate lambda:
+[](gui_button_t* self) { self->on_pressed(); }
you can use as-is boost.python, since normal pointer function. however, want solution work any callable member. why just support boost::function
when can support anything?
we'll start @columbo's closure_traits
, additionally adding way pull out argument list;
template <typename...> struct typelist { }; template <typename c, typename r, typename... args> \ struct closure_traits<r (c::*) (args... rem_ctor var) cv> \ { \ using arity = std::integral_constant<std::size_t, sizeof...(args) >; \ using is_variadic = std::integral_constant<bool, is_var>; \ using is_const = std::is_const<int cv>; \ \ using result_type = r; \ \ template <std::size_t i> \ using arg = typename std::tuple_element<i, std::tuple<args...>>::type; \ \ using args = typelist<args...>; \ };
then we'll write wrapper callable member. since our lambda can take no capture, have take callable template parameter:
template <typename cls, typename f, f cls::*callable> class wrap { ... };
i use c++14's auto
return type deduction save typing. make top-level make_pointer()
static member function forwards helper member function additionally takes arguments. full wrap
looks like:
template <typename cls, typename f, f cls::*callable> class wrap { public: static auto make_pointer() { return make_pointer_impl(typename closure_traits<f>::args{}); } private: template <typename... args> static auto make_pointer_impl(typelist<args...> ) { // here our lambda takes cls first argument // , rest of callable's arguments, // , calls return +[](cls* self, args... args) { return (self->*callable)(args...); }; } };
which can use wrap button:
void (*f)(gui_button_t*) = wrap<gui_button_t, decltype(gui_button_t::on_pressed), &gui_button_t::on_pressed >::make_pointer();
that's little verbose , repetitive, let's make macro (sigh):
#define wrap_mem(cls, mem) wrap<cls, decltype(cls::mem), &cls::mem>::make_pointer()
so get:
void (*f)(gui_button_t*) = wrap_mem(gui_button_t, on_pressed); f(some_button); // calls some_button->on_pressed()
since gives pointer function, can use directly normal boost.python api:
class_<gui_button_t>("guibutton", init<>()) .def("on_pressed", wrap_mem(gui_button_t, on_pressed));
demo demonstrating function pointers member std::function
, member struct
operator()
.
the above gets ability expose callable. if want additionally able assignment, i.e.:
button = guibutton() button.on_pressed = callback_function button.on_pressed()
we'll need else. can't expose operator=
in meaningful way in python, support above functionality, you'd have override __setattr__
instead. now, if open to:
button.set_on_pressed(callback_function)
we extend above wrap
solution add setter, implementation be, in vein of above:
static auto set_callable() { return make_setter_impl( typelist<typename closure_traits<f>::result_type>{}, typename closure_traits<f>::args{}); } template <typename r, typename... args> static auto make_setter_impl(typelist<r>, typelist<args...> ) { return +[](cls* self, py::object cb) { (self->*callable) = [cb](args... args) { return py::extract<r>( cb(args...))(); }; }; } // need separate overload void template <typename... args> static auto make_setter_impl(typelist<void>, typelist<args...> ) { return +[](cls* self, py::object cb) { (self->*callable) = [cb](args... args) { cb(args...); }; }; } #define set_mem(cls, mem) wrap<cls, decltype(cls::mem), &cls::mem>::set_callable()
which expose via:
.def("set_on_pressed", set_mem(button, on_pressed))
however, if insist on supporting direct-assignment, need additionally expose like:
static void setattr(py::object obj, std::string attr, py::object val) { if (attr == "on_pressed") { button& b = py::extract<button&>(obj); set_mem(button, on_pressed)(&b, val); } else { py::str attr_str(attr); if (pyobject_genericsetattr(obj.ptr(), attr_str.ptr(), val.ptr()) { py::throw_error_already_set(); } } } .def("__setattr__", &button::setattr);
that work, you'd have add more cases each functor want set. if have 1 functor-like object per class, not big deal, , can write higher order function product specific setattr
-like function given attribute name. if have multiples, it's going steadily worse simple set_on_pressed
solution.
if c++14 not available, we'll have explicitly specify return type of make_pointer
. we'll need few handy type traits. concat
:
template <typename t1, typename t2> struct concat; template <typename t1, typename t2> using concat_t = typename concat<t1, t2>::type; template <typename... a1, typename... a2> struct concat<typelist<a1...>, typelist<a2...>> { using type = typelist<a1..., a2...>; };
and turn return type , typelist
function pointer:
template <typename r, typename t> struct make_fn_ptr; template <typename r, typename... args> struct make_fn_ptr<r, typelist<args...>> { using type = r(*)(args...); }; template <typename r, typename t> using make_fn_ptr_t = typename make_fn_ptr<r, t>::type;
and within wrap
, can define result type as:
using r = make_fn_ptr_t< typename closure_traits<f>::result_type, concat_t< typelist<cls*>, typename closure_traits<f>::args > >;
and use instead of auto
. c++11 demo.
Comments
Post a Comment