Предположим, у меня есть функция под названием 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
Но, к сожалению, я не могу сделать эту параметризацию этой вещи (как предложено должно работать в документация по композиции с использованием 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));
}
Вы связываете функцию (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 также удобство, которое может быть сделано без дополнительной работы.