1

How to create a packaged_task of a function object ?

For example,

class TaskA {
    public:
        std::function<int(void)> func;
        TaskA(std::function<int(void)>&f) : func(f) {}
        int res;
        int operator()() {
            std::this_thread::sleep_for(std::chrono::milliseconds(2000));
            return func();
        }
};

Now create a function object first:

std::function<int(void)> func1 = xy; // xy is some function
TaskA task1(func1);

Now I have a function that takes this function object as a parameter, creates a packaged_task and returns a future.

template<typename TaskType>
auto handle(TaskType func) -> std::future</*correct syntax ?*/>
{
    auto task  = std::packaged_task</*correct syntax ?*/>(std::move(func));
    /* correct syntax ? */
    auto futur = task.get_future();

    // do something with the task

    return std::move(futur);
}

and later use this handle function as follows:

auto x = handle(task1);

Can you please help me with the proper syntax for the future as well as the packaged_task declaration?

Thanks !

Debashish
  • 1,155
  • 19
  • 34

2 Answers2

1

This uses a metafunction to deduce the signature. Note that I didn't test this with other types of callable, so whilst it should work for anything with an operator(), it might need some work for other kinds of callable.

#include <functional>
#include <future>

class TaskA {
public:
    int operator()() { return 0; }
};

template <typename T>
struct function_signature
    : public function_signature<decltype(&T::operator())>
{};

template <typename ClassType, typename ReturnType, typename... Args>
struct function_signature<ReturnType(ClassType::*)(Args...)> {
    using type = ReturnType(Args...);
};

template<typename TaskType>
auto handle(TaskType func)
{
    using signature = typename function_signature<TaskType>::type;
    auto task  = std::packaged_task<signature>(std::move(func));

    auto futur = task.get_future();

    // do something with the task

    return std::move(futur);
}

int main() {
    TaskA task1;
    handle(task1);
}
Ayjay
  • 3,413
  • 15
  • 20
1

I would try to be explicit about return type, function type and call signature of your task, and would put it as part of the class interface. For simplicity I do all explicit, but this could be done via template or using decltype feature:

#include <cmath>
#include <iostream>
#include <map>
#include <functional>
#include <future>
#include <cmath>
#include <thread>
//#include <gsl/gsl>
using namespace std;

int f()
{
    return 3;
}

class TaskA
{
public:
    using ReturnType = int;
    using Function = std::function<ReturnType(void)>;
    typedef ReturnType (CallSignature)();

    TaskA(Function& f) : func{f} {}


    ReturnType operator()()
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
        return func();
    }

private:
    Function func;
};

template<typename TaskType>
auto handle(TaskType func) -> std::future<typename TaskType::ReturnType>
{
    auto task = std::packaged_task<typename TaskType::CallSignature>{func};
    auto future = task.get_future();
    // do  something with task
    task();
    return future;
}

int main()
{
    std::function<int()> ff{f};
    TaskA task{ff};
    auto result = handle(task);
    cout << result.get() << endl;
}

live example here

StPiere
  • 4,113
  • 15
  • 24
  • Thanks! I tried to modify the function `f` with one `int` argument but it shows compilation error. Can you help? https://wandbox.org/permlink/Yyn4dMkkZbQCZWNd – Debashish Sep 15 '19 at 04:58
  • 1
    you have to be careful: CallSignature is corresponding to your operator()() -> it still has no int parameter. I've updated code in your link. If you would declare operator()(int), then CallSignature would accept an int too, as in your first version. – StPiere Sep 15 '19 at 16:44