Search code examples
c++perfect-forwardingpackaged-task

Perfect forwarding and packaged_task wrapper


I'm trying to wrap packaged_task in a generic class, but having trouble initializing it with a generic function. I've gotten it to work for a specific one, but I want it to be more abstract. Just an fyi, if you uncomment the 2 lines of code I commented out, the code runs fine. My guess is, I'm attempting to utilize the template parameters incorrectly.

EDIT: Made some additions so that this actually works, but the same issue persists. So, if I try to pass a function into the ctor of my class, I get a "bad function call" when I attempt to call ret.get(). HOWEVER, if I name the function directly, it works.

EDIT2.0: To make this really easy, all I want to know here is why calling tsk(func) doesn't work, and tsk(countdown) does? And how to make tsk(func) work...

int countdown (int from, int to) {
    for (int i=from; i!=to; --i) {
        std::cout << i << '\n';
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    std::cout << "Lift off!\n";
    return from-to;
}

template<typename> class packaged_task_wrapper;

template<typename T, typename... Args>
class packaged_task_wrapper<T(Args...)> {
public:

    template<typename ...Ts>
    explicit packaged_task_wrapper(Ts &&... ts) : func(forward<Ts>(ts)...) {

        packaged_task<T(Args...)> tsk(func);       // THIS DOES NOT WORK
        //packaged_task<T(Args...)> tsk(countdown);  // THIS WORKS

        future<T> ret = tsk.get_future();          // get future
        thread this_thread (move(tsk),3,0);        // spawn thread to count down from 3 to 0
        int value = ret.get();                     // wait for the task to finish and get result
        // ...

        cout << "The countdown lasted for " << value << " seconds.\n";
        this_thread.join();
    }
};


int main ()
{
    packaged_task_wrapper<int(int,int)>(countdown);

    return 0;
}

Solution

  • Why not use std::async? If all you want is to run the function on a different thread then this will do the trick.

    auto future_result = std::async(std::launch::async,
                                    [&](){ return countdown(3, 0); });
    future_result.get();
    

    If you don't want to use async then this should work:

    template<typename> class packaged_task_wrapper;
    
    template<typename T, typename... Args>
    class packaged_task_wrapper<T(Args...)> {
    public:
    
        template <typename F>
        explicit packaged_task_wrapper(F&& f) {
    
            packaged_task<T(Args...)> task(std::forward<F>(f));
            future<T> ret = task.get_future();       // get future
            thread this_thread (move(task), 10, 8);  // spawn thread to count down from 3 to 0
            T value = ret.get();                     // wait for the task to finish and get result
            // ...
    
            std::cout << "The countdown lasted for " << value << " seconds.\n";
            this_thread.join();
        }
    };