Boost.Python的和Boost.Function [英] Boost.Python and Boost.Function

查看:153
本文介绍了Boost.Python的和Boost.Function的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想换一个的boost ::功能类成员,使其可以通过以下方式使用:

I want to wrap a boost::function class member so that it can be used in the 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);

然后在Python:

And then in Python:

def callback_function():
    print 'button has been pressed'

button = GuiButton()
button.on_pressed = callback_function
button.on_pressed() # function should be callable from C++ or Python

不过,这个尝试对于产生类模板参数错误的大量等等。

However, trying this yields an enormous amount of errors regarding class template parameters and so on.

我已经做了一些搜索,但一直没能找到一个答案我一直在寻找。下面这篇文章几分接近,但他们没有关于这个问题的直接接触。

I've done a bit of searching but haven't been able to find an answer I've been looking for. The following article sort of come close but they don't touch directly on the subject.

http://bfroehle.com/2011 / 07/18 /升压蟒蛇和升压功能-II /

我在做什么错在这里?什么我需要做的就是为这个功能所需要的接口?

What am I doing wrong here? What do I need to do to get the desired interface for this functionality?

提前非常感谢。

推荐答案

Boost.Python的只接受函数指针和指向成员函数。所以我们需要做的是转变我们的赎回成一个函数指针。这里的关键思想是

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


  1. 有没有捕捉可以转换为一个函数指针(通过魔法)一个lambda

  2. 函数指针PTED相同的方式,在Python成员函数间$ P $:第一个参数是

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

您已经可以使用它作为-是Boost.Python的,因为这是一个完全正常的函数指针。然而,我们需要一个解决方案,将用于的任何的可调用的成员工作。为什么的只是的支持的boost ::功能时,你可以支持什么?

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?

我们将与 @科伦坡的 closure_traits 启动,但另外加入的方式拉出的参数列表;

We'll start with @Columbo's closure_traits, but additionally adding a way to pull out the 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...>;                                        \
};

然后,我们会写任何可调用成员的包装。由于我们的lambda可以不采取捕获,我们的有无的采取调用作为模板参数:

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>
class wrap { ... };

我将使用C ++ 14的汽车返回类型推演,以节省一些打字。我们做一个顶级 make_pointer()静态成员函数,仅仅转发到一个辅助成员函数需要额外的参数。完整的包装如下:

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>
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 is our lambda that takes the CLS as the first argument
        // and then the rest of the callable's arguments,
        // and just calls it
        return +[](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):

#define WRAP_MEM(CLS, MEM) wrap<CLS, decltype(CLS::MEM), &CLS::MEM>::make_pointer()

所以我们得到:

void (*f)(gui_button_t*) = WRAP_MEM(gui_button_t, on_pressed);

f(some_button); // calls some_button->on_pressed()

由于这给了我们一个函数指针,我们可以用正常的Boost.Python的API直接使用这样的:

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));

演示 证明函数指针到成员的std ::功能和成员结构运算符()

上面让你有能力的暴露的一个调用。如果你想还能够做任务,即:

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()

我们需要做些别的事情。你不能暴露运算符= 在Python中的有意义的方式,这样可以支持上述功能,你就必须重写 __ SETATTR __ 来代替。现在,如果你是开放的:

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)

我们可以延长上述包装解决方案添加setter,它的实施将在上述的静脉:

we could extend the above wrap solution to add a setter, whose implementation would be, in the vein of the 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 a separate overload just for 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 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:

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);

这会工作,但你不得不添加更多的情况下,你要设置的每个仿函数。如果你只有每类中的一个仿函数类对象,可能不是什么大不了的事,甚至可以写一个高阶函数产品的特定 SETATTR 般的功能对于一个给定属性名称。但如果你有倍数,它会不断得到比简单的 set_on_ pressed 解决方案更糟。

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.

如果C ++ 14不可用,我们必须只明确指定 make_pointer 的返回类型。我们需要一些方便的类型特征。 CONCAT

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>
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...>;
};

然后东西打开返回类型和类型串成一个函数指针:

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 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
                    >
                >;

和用它来代替汽车 C ++ 11演示

and use that instead of auto. C++11 Demo.

这篇关于Boost.Python的和Boost.Function的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆