Вопрос

I am playing with an example 4.14 from the book Antony Williams - C + + Concurrency in Action in which emulated std::async using std::packaged_task and std::thread.

Why this code does not compile when I uncomment lines and how to rewrite template to make it work?

#include <iostream>
#include <future>
#include <thread>
#include <string>

void func_string(const std::string &x) {}

void func_int(int x) {}

template <typename F, typename A>
std::future<typename std::result_of<F(A&&)>::type> spawn_task(F &&f, A &&a) {
  typedef typename std::result_of<F(A&&)>::type result_type;
  std::packaged_task<result_type(A&&)> task(std::move(f));
  std::future<result_type> res(task.get_future());
  std::thread t(std::move(task), std::move(a));
  t.detach();
  return res;
}


int main () {
  std::string str = "abc";

  // auto res1 = spawn_task(func_string, str);
  // res1.get();
  auto res2 = spawn_task(func_int, 10);
  res2.get();

  return 0;
}

Compilation errors:

nnovzver@archer /tmp $ clang++ -std=c++11 -lpthread temp.cpp && ./a.out
In file included from temp.cpp:2:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/future:38:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/functional:1697:56: error: no type
      named 'type' in 'std::result_of<std::packaged_task<void (std::basic_string<char> &)> (std::basic_string<char>)>'
      typedef typename result_of<_Callable(_Args...)>::type result_type;
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/thread:135:41: note: in instantiation
      of template class 'std::_Bind_simple<std::packaged_task<void (std::basic_string<char> &)>
      (std::basic_string<char>)>' requested here
        _M_start_thread(_M_make_routine(std::__bind_simple(
                                        ^
temp.cpp:15:15: note: in instantiation of function template specialization 'std::thread::thread<std::packaged_task<void
      (std::basic_string<char> &)>, std::basic_string<char> >' requested here
  std::thread t(std::move(task), std::move(a));
              ^
temp.cpp:24:15: note: in instantiation of function template specialization 'spawn_task<void (&)(const
      std::basic_string<char> &), std::basic_string<char> &>' requested here
  auto res1 = spawn_task(func_string, str);
              ^
In file included from temp.cpp:2:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/future:38:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/functional:1726:50: error: no type
      named 'type' in 'std::result_of<std::packaged_task<void (std::basic_string<char> &)> (std::basic_string<char>)>'
        typename result_of<_Callable(_Args...)>::type
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
2 errors generated.
Это было полезно?

Решение

If you look at the documentation of async, the feature you want to emulate, it decays all the future template arguments. And it is effective as it works far better like this, as show here :

template <typename T_>
using decay_t = typename std::decay<T_>::type;
template< class T >
using result_of_t = typename std::result_of<T>::type;

template <typename F, typename A>
std::future<result_of_t<decay_t<F>(decay_t<A>)>> spawn_task(F &&f, A &&a) {
    using result_t = result_of_t<decay_t<F>(decay_t<A>)>;
    std::packaged_task< result_t(decay_t<A>)> task(std::forward<F>(f));
    auto res = task.get_future();
    std::thread t(std::move(task), std::forward<A>(a));
    t.detach();
    return res;
}

Другие советы

First of all std::packaged_task wraps any Callable target function. And one of the requirements of Callable is that it has a suitable return value. void is not a suitable return value. I changed your functions to return int for test purposes:

int func_string(const std::string &x) 
{
    return 1;
}

int func_int(int x) 
{
    return 2;
}

Secondly it looks to me like you've got a mismatch between your actual function signatures and what you are specifying in the std::result_of<> templates. Specifically your function signatures are:

int func_string(const std::string &x);
int func_int(int x);

but you are passing:

std::result_of<F(A&&)>::type result_type

changing that to:

std::result_of<F(A)>::type result_type

And everything compiles.

Here is the full source code (minus the headers):

int func_string(const std::string &x) 
{
    return 1;
}

int func_int(int x) 
{
    return 2;
}

template <typename F, typename A>
std::future<typename std::result_of<F(A)>::type> spawn_task(F &&f, A &&a) 
{
    typedef typename std::result_of<F(A)>::type result_type;
    std::packaged_task<result_type(A)> task(std::move(f));
    std::future<result_type> res(task.get_future());
    std::thread t(std::move(task), std::move(a));
    t.detach();
    return res;
}

int main()
{
    std::string str = "abc";

     auto res1 = spawn_task(func_string, str);
     res1.get();
    auto res2 = spawn_task(func_int, 10);
    res2.get();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top