Отправлять обратные вызовы в очередь задач, используя boost :: bind

Предположим, у меня есть функция под названием 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

Решение

У меня нет ответа. Тем не менее, я играл с этим более часа:

  • 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
}

Вот полностью скомпилированное демо, показывающее положение вещей: Жить на Колиру

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

Я перешел на феникс bind потому что это позволяет связывать полиморфные функциональные объекты (которые bind выше есть).

Это решение требует decltype, Он также использует переменную, но это может быть подделано с перегрузками до N аргументов. Rvalue refs также удобство, которое может быть сделано без дополнительной работы.

5

По вопросам рекламы [email protected]