4

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

2 回答 2

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 也是一种方便,无需更多工作即可完成。

于 2013-09-30T19:11:17.193 回答
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

但是,可悲的是,我似乎无法将这个东西参数化(正如建议的那样,应该在使用 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));
}
于 2013-09-08T15:28:41.947 回答