2013-09-08 7 views
4

Предположим, у меня есть функция с именем subscribe(), которая принимает обработчик обратного вызова, который будет вызываться при срабатывании события.Отправлять обратные вызовы в очередь задач с помощью boost :: bind

Теперь у меня есть другая версия, называемая subscribe2(). Все одно и то же, за исключением того, что при запуске ему необходимо отправить его в очередь событий. Он реализован с использованием оригинала subscribe(), с вспомогательным функционалом, который называется helper(). Все, что он делает, это привязать исходный обработчик и любые дополнительные аргументы к функтору и вызвать postToEventQueue().

Теперь мне интересно, если есть способ устранить вспомогательную функцию, так что в subsribe2(), я могу как-то упаковать postToTaskQueue() функции и оригинальный обработчик обратного вызова напрямую, и передать его в subscribe(). Причина в том, что у меня много разных типов обработчиков, и утомительно и утомительно вводить вспомогательную функцию по всему месту. Afterall, 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)); 
} 

ответ

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)); 
} 
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 - также удобство, которое можно сделать без небольшой работы.

+0

Как я пропустил этот ответ. Мне нравится ваше объяснение (как это часто бывает), и хотя он по-прежнему требует «помощников», по крайней мере, он использует только общие, многоразовые. – sehe