Boost.python And Boost.function
Solution 1:
Boost.Python only accepts pointers to functions and pointers to member functions. So what we need to do is convert our callable into a function pointer. The key ideas here are that
- a lambda that has no capture can be converted to a function pointer (via sorcery)
- function pointers are interpreted the same way as member functions in Python: the first argument is self
So in your case, what we need to do is generate this lambda:
+[](gui_button_t* self) {
    self->on_pressed();
}
You can already use that as-is with Boost.Python, since that is a perfectly normal pointer to function. However, we want a solution that will work for any callable member. Why just support boost::function when you can support anything? 
We'll start with @Columbo's closure_traits, but additionally adding a way to pull out the argument list;
template <typename...> structtypelist { };
template <typename C, typename R, typename... Args>                        \
structclosure_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 a wrapper for any callable member. Since our lambda can take NO capture, we have to take the callable as a template parameter:
template <typename CLS, typename F, F CLS::*callable>
classwrap { ... };
I will use C++14's auto return type deduction to save some typing. We make a top-level make_pointer() static member function that just forwards to a helper member function that additionally takes the arguments. The full wrap looks like:
template <typename CLS, typename F, F CLS::*callable>
classwrap {
public:
    staticautomake_pointer(){
        returnmake_pointer_impl(typename closure_traits<F>::args{});
    }
private:
    template <typename... Args>
    staticautomake_pointer_impl(typelist<Args...> ){
        // here is our lambda that takes the CLS as the first argument// and then the rest of the callable's arguments,// and just calls itreturn +[](CLS* self, Args... args) {
            return (self->*callable)(args...);
        };
    }
};
Which we can use to wrap your 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 a little verbose and repetitive, so let's just make a macro (sigh):
#defineWRAP_MEM(CLS, MEM) wrap<CLS, decltype(CLS::MEM), &CLS::MEM>::make_pointer()
So we get:
void (*f)(gui_button_t*) = WRAP_MEM(gui_button_t, on_pressed);
f(some_button); // calls some_button->on_pressed()Since this gives us a pointer to function, we can use this directly with the normal Boost.Python API:
class_<gui_button_t>("GuiButton", init<>())
    .def("on_pressed", WRAP_MEM(gui_button_t, on_pressed));
Demo demonstrating function pointers to a member std::function and a member struct with an operator(). 
The above gets you the ability to expose a callable. If you want to additionally be able to do assignment, i.e.:
button = GuiButton()
button.on_pressed = callback_function
button.on_pressed()
We'll need to do something else. You can't expose operator= in a meaningful way in Python, so to support the above functionality, you'd have to override __setattr__ instead. Now, if you were open to:
button.set_on_pressed(callback_function)
we could extend the above wrap solution to add a setter, whose implementation would be, in the vein of the above:
staticautoset_callable(){
    returnmake_setter_impl(
        typelist<typename closure_traits<F>::result_type>{},
        typename closure_traits<F>::args{});
}
template <typename R, typename... Args>
staticautomake_setter_impl(typelist<R>, typelist<Args...> ){
    return +[](CLS* self, py::object cb) {
        (self->*callable) = [cb](Args... args) {
            return py::extract<R>(
                cb(args...))();
        };
    };
}
// need a separate overload just for voidtemplate <typename... Args>
staticautomake_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 you could then expose via:
.def("set_on_pressed", SET_MEM(button, on_pressed))
However, if you insist on supporting direct-assignment, then you would need to additionally expose something like:
staticvoid 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 would work, but you'd have to add more cases for each functor you want to set. If you only have one functor-like object per class, probably not a big deal, and can even write a higher order function to product a specific setattr-like function for a given attribute name. But if you have multiples, it's going to steadily get worse than the simple set_on_pressed solution. 
If C++14 is not available, we'll have to just explicitly specify the return type of make_pointer. We'll need a few handy type traits. concat:
template <typename T1, typename T2>
structconcat;
template <typename T1, typename T2>
usingconcat_t = typename concat<T1, T2>::type;
template <typename... A1, typename... A2>
structconcat<typelist<A1...>, typelist<A2...>> {
    using type = typelist<A1..., A2...>;
};
And then something to turn a return type and a typelist into a function pointer:
template <typename R, typename T>
structmake_fn_ptr;
template <typename R, typename... Args>
structmake_fn_ptr<R, typelist<Args...>> {
    using type = R(*)(Args...);
};
template <typename R, typename T>
usingmake_fn_ptr_t = typename make_fn_ptr<R, T>::type;
And then within wrap, we can just define a 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 that instead of auto. C++11 Demo.
Post a Comment for "Boost.python And Boost.function"