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

Post callbacks to a task queue using boost::bind

本文关键字:任务队列 boost bind 使用 回调      更新时间:2023-10-16

>假设我有一个名为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));
}

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

  • 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

但是,可悲的是,我似乎无法使这件事参数化(正如建议的那样,应该在使用嵌套绑定的合成文档中工作):

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

你正在绑定一个函数(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 也是一种便利,无需多做一点工作即可完成。