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

  1. a lambda has no capture can converted function pointer (via sorcery)
  2. 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

Popular posts from this blog

angularjs - ADAL JS Angular- WebAPI add a new role claim to the token -

php - CakePHP HttpSockets send array of paramms -

node.js - Using Node without global install -