使用boost :: bind将回调发布到任务队列

时间:2013-09-08 03:31:46

标签: c++ boost-bind boost-function boost-phoenix boost-lambda

假设我有一个名为subscribe()的函数,它接受一个回调处理程序,当事件被触发时将被调用。

现在,我有另一个名为subscribe2()的版本。一切都是相同的,除了触发时,它需要将其发布到事件队列。它是使用原始subscribe()实现的,其中包含一个名为helper()的帮助函数。它所做的就是将原始处理程序和任何其他参数绑定到仿函数中,并调用postToEventQueue()

现在,我想知道是否有消除辅助函数的方法,因此在subsribe2()中,我可以以某种方式直接打包postToTaskQueue()函数和原始回调处理程序,并传递它到subscribe()。原因是我有很多不同的处理程序类型,并且在整个地方引入辅助函数是乏味和累人的。毕竟,boost :: bind应该在给定原始函数的情况下返回一个新函数,对吧?我试图用boost :: bind直接生成辅助函数。

一种尝试是说

subscribe(boost::bind(boost::bind(postToTaskQueue, boost::bind(_1, _2)), cb, _1)); 
subscribe2()中的

,但它不起作用。它有可能吗?

请参阅下面的详细示例代码。谢谢!

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <iostream>

typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;

void handler(int i){
 std::cout << "i=" << i <<std::endl;
}

void subscribe(SomeCallback cb)
{
  cb(100);  //just invoke the callback for simplicity
}

void postToTaskQueue(Task t)
{
   t();  // just invoke the task for simplicity
}

void helper(SomeCallback cb, int i)
{
   Task t = boost::bind(cb, i);
   postToTaskQueue(t);
}

void subscribe2(SomeCallback cb)
{
  subscribe(boost::bind(helper, cb, _1));

  // this does not work..
  // subscribe(boost::bind(boost::bind(postToTaskQueue, boost::bind(_1, _2)), cb, _1)); 
}
int main()
{
  subscribe(boost::bind(handler, _1));
  subscribe2(boost::bind(handler, _1));
}

2 个答案:

答案 0 :(得分:5)

我没有回答。但是,我玩了一个多小时了:

  • boost::bind
  • boost::apply<>
  • boost::protect

也许,只是也许,一个更有经验的推动开发者可以从这里接受它:

void subscribe2(SomeCallback cb)
{
    using boost::bind;
    using boost::protect;
    using boost::apply;

    bind(cb, 41)(); // OK of course
    postToTaskQueue(bind(cb, 46)); // also fine
    bind(postToTaskQueue, protect(bind(cb, 146)))(); // boost::protect to the rescue

    postToTaskQueue(bind(apply<void>(), cb, 47));
    bind(postToTaskQueue, protect(bind(apply<void>(), cb, 147)))();

以上打印

i=41
i=46
i=146
i=47
i=147

但是,遗憾的是,我似乎无法将此事件参数化(如建议应在the documentation on composition using Nested Binds中使用):

    // but sadly, this appears to not work ...
    auto hmm = bind(postToTaskQueue, bind(apply<void>(), cb, _1));
    hmm(997); // FAIL
}

这是一个完整编译的演示,展示了事态: Live on Coliru

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/bind/apply.hpp>
#include <iostream>

typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()>    Task;

void handler(int i){
    std::cout << "i=" << i <<std::endl;
}

void subscribe(SomeCallback cb)
{
    cb(100);  //just invoke the callback for simplicity
}

void postToTaskQueue(Task t)
{
    t();  // just invoke the task for simplicity
}

void helper(SomeCallback cb, int i)
{
    postToTaskQueue(boost::bind(cb, i));
}

void subscribe2(SomeCallback cb)
{
    using boost::bind;
    using boost::protect;
    using boost::apply;

    bind(cb, 41)(); // OK of course
    postToTaskQueue(bind(cb, 46)); // also find
    bind(postToTaskQueue, protect(bind(cb, 146)))(); // boost::protect to the rescue

    postToTaskQueue(bind(apply<void>(), cb, 47));
    bind(postToTaskQueue, protect(bind(apply<void>(), cb, 147)))();

    // but sadly, this appears to not work ...
    auto hmm = bind(postToTaskQueue, bind(apply<void>(), cb, _1));
    //hmm(997); // FAIL
}
int main()
{
    subscribe (boost::bind(handler, _1));
    subscribe2(boost::bind(handler, _1));
}

答案 1 :(得分:5)

您绑定了一个本身进行绑定的函数(helper)。这意味着你(间接)绑定bind本身。这是关键的洞察力。解决方案是编写一个可以绑定的小bind函数对象包装器。这是我的解决方案的样子:

#include <utility>
#include <iostream>
#include <boost/function.hpp>
#include <boost/phoenix/bind.hpp>
#include <boost/phoenix/core/argument.hpp>
using boost::phoenix::placeholders::_1;
typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;

struct bind_t
{
    template<typename Sig>
    struct result;

    template<typename This, typename ...A>
    struct result<This(A...)>
    {
        typedef decltype(boost::phoenix::bind(std::declval<A>()...)) type;
    };

    template<typename ...A>
    auto operator()(A &&...a) const -> decltype(boost::phoenix::bind(std::forward<A>(a)...))
    {
        return boost::phoenix::bind(std::forward<A>(a)...);
    }
};

bind_t const bind = {};

void handler(int i)
{
    std::cout << "i=" << i <<std::endl;
}

void subscribe(SomeCallback cb)
{
    cb(100);  //just invoke the callback for simplicity
}

void postToTaskQueue(Task t)
{
    t();  // just invoke the task for simplicity
}

void subscribe2(SomeCallback cb)
{
    subscribe(bind(postToTaskQueue, bind(bind, cb, _1)));
}

int main()
{
    subscribe(::bind(handler, _1));
    subscribe2(::bind(handler, _1));
}

我切换到Phoenix的bind因为它允许你绑定多态函数对象(上面bind)。

此解决方案需要decltype。它也使用可变参数,但可以使用多达N个参数的重载进行伪造。 Rvalue refs也是一种便利,无需多花一些工作即可完成。