4

i am trying to build some wrapper around Glib::Dispatcher to dispatch any functional type into the dispatcher. I want to have some function dispatch that can transfer a function into the Glib main loop:

template<class Function, class ...Args>
std::future<typename std::result_of<Function(Args...)>::type>
dispatch(Function &&f, Args &&...args);

This function would create a packaged task from f(args) and return its future:

std::packaged_task<typename std::result_of<Function(Args...)>::type()> task(f(args...));
return task.get_future();

I need to create now from this task a std::packaged_task<void()> to put them into one std::queue so that the connected function to Glib::Dispatcher can execute them.

My question is: How can i create from a std::packaged_task<R()> a std::packaged_task<void()> in two steps, so that i can return from the first task its future and put the second one into a queue of std::queue<std::packaged_task<void()>> type?

Philipp H.
  • 1,513
  • 3
  • 17
  • 31
  • It also can be of other functor type instead of `std::packaged_task`; They all just have to hide `f` and `args...`. – Philipp H. Jan 15 '13 at 10:10
  • I have found a way to implement that using type erasure, but is there some way to use the stl for my problem? – Philipp H. Jan 16 '13 at 07:21

1 Answers1

6

Firstly, to have std::packaged_task invoke a particular function with a given argument set then either (a) you need to bind the function to the arguments before you pass it to std::packaged_task, or (b) you need to pass the arguments to the function call operator of the std::packaged_task object. In your code, you are passing f(args...) to the std::packaged_task constructor, which will call the function right there and then pass the result to the constructor, which is probably not what you want and may not even compile.

Secondly, if you bind the arguments at construction time then your std::packaged_task instance is a callable object that takes no parameters and returns void, so can be moved directly into a std::packaged_task<void()>.

Putting this all together, we get:

std::queue<std::packaged_task<void()>> task_queue;

template<class Function, class ...Args>
std::future<typename std::result_of<Function(Args...)>::type>
dispatch(Function &&f, Args &&...args) {
    std::packaged_task<typename std::result_of<Function(Args...)>::type()> task(
        std::bind(f,args...));
    auto res=task.get_future();
    task_queue.push(std::packaged_task<void()>(std::move(task)));
    return res;
}
Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
  • 1
    Note that this doesn't work with Microsoft's STL https://developercommunity.visualstudio.com/content/problem/108672/unable-to-move-stdpackaged-task-into-any-stl-conta.html – znkr May 10 '20 at 16:24
  • It's old question. But i decided write how works std::packaged_task(std::move(task)) expression since C++17. if result_of - is void -- it simply moved. otherweice it calls template packaged_task(Func&&) constructor, where Func=std::packaged_task . In practise packaged_task - is std::shared_ptr < ...> , and second case there allocated shared_ptr< shared_ptr < ... > > :) – Khurshid Normuradov Jan 18 '23 at 15:53