Search code examples
c++c++11boostboost-asiovariadic-templates

Boost::ASIO and std::packaged_task


I have the following pieces of C++14 code

boost::asio::io_service service_;

I want to submit pieces of work into the io_service, using the following code that takes any function, it's input arguments and returns me a std::future to the return value.

template <typename F, typename... Args>
auto enqueue(F &&f, Args &&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
  typedef typename std::result_of<F(Args...)>::type rType;
  auto task = std::make_shared<std::packaged_task<rType()>>(std::bind(std::forward<F>(f),
                                                                       std::forward<Args>(args)...));
  std::future<rType> res = task->get_future();
  service_.post(task);
  return res;
}

This was then called using

enqueue([] (int i) {
  return i+1;
}, 100);

This doesnt seem to be working. I get an error saying that service_.post() was not expecting this input.

xxxxxxxxxxxxxx:49:3:   required from ‘std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> enqueue(F&&, Args&& ...) [with F = main()::<lambda()>::<lambda()>; Args = {int}; typename std::result_of<_Functor(_ArgTypes ...)>::type = int]’
xxxxxxxxxxxxxx:44:6:   required from here
/usr/include/boost/asio/impl/io_service.hpp:102:3: error: static assertion failed: CompletionHandler type requirements not met
   BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;
   ^
/usr/include/boost/asio/impl/io_service.hpp:85:3: error: no match for call to ‘(std::shared_ptr<std::packaged_task<void()> >) ()’
   BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;
   ^

As far as I understand the boost::asio documentation, this can be done. Any ideas?


Solution

  • From the documentation for post():

    handler

    The handler to be called. The io_service will make a copy of the handler object as required. The function signature of the handler must be: void handler();

    You are passing in a std::shared_ptr<std::packaged_task<int()>>. A shared_ptr doesn't have operator() defined. And packaged_task unwrapped isn't copyable.

    So in order to make this work, you'll have to make a shared_ptr<promise>:

    using R = std::result_of_t<F(Args&&...)>;
    auto promise = std::make_shared<std::promise<R>>();
    std::future<R> res = promise->get_future();
    
    service.post([promise = std::move(promise),
        f = std::forward<F>(f),
        args = std::make_tuple(std::forward<Args>(args)...)]{
            promise->set_value(std::experimental::apply(f, args));    
        });
    return res;