2
votes

I simply try to create a std::packaged_task for a given std::bind.

#include <functional>
#include <future>

class A
{
public:
  template<class T>
  void execute(T func)
  {
    std::packaged_task<T> task(func);
  }
};

int main()
{
  auto func = std::bind([](int x) { return x*x; }, 5);

  A name;

  name.execute(func);
}

main.cpp: In instantiation of 'void A::execute(T) [with T = std::_Bind(int)>]': main.cpp:20:20: required from here main.cpp:10:36: error: 'std::packaged_task(int)> > task' has incomplete type std::packaged_task task(func);

I'm using G++ 5.2.0, and C++14.

Does someone have an idea?

Thank you,

2
How is func supposed to be called? And what is it supposed to return, if any?T.C.
auto is not allowed in a parameter declaration.0x499602D2
The problem is that it doesn't compileM. Christopher
T.C, it should return anything, depending of the std::bindM. Christopher
I see a bunch of problems in your code almost completely unrelated to your problem. Your use of auto is not compliant to C++11. Your use of decltype indicates to me that you do not know what it does. I'm not sure how "in theory this should work" means to you. You have also not understood T.C.s question: the func passed in, how would you expect it to be called? Do you expect to pass it an integer? Nothing? A string? Three chickens and a goat? The result of std::bind doesn't answer the question.Yakk - Adam Nevraumont

2 Answers

4
votes

As of c++11 it is not possible to declare function arguments as auto (I believe its part of c++17). Unfortunately I don't have a compiler which supports that feature and I'm not quite sure of its semantics but a workaround would be to use templates:

#include <future>
#include <functional>

template<class T>
void execute(std::function<T> func) // is a std::bind
{
    std::packaged_task<T> task(func);
    //...
}

which you can use like this:

std::function<int()> func = std::bind([](int x) { return x*x; }, 5);
execute(func);

The problem why you get this error is that std::packaged_task is only specialized for template arguments of the form ReturnType(Args...). So if you pass something different like the std::_Binder returned from std::bind or a std::function it appears as undefined.

Edit

Concerning your comment about deducing the type of the std::function:

Instead of using std::bind() you could use lambdas.

If you have a class A with the function doSomething(int x) like in your comment:

class A
{
public:
    int doSomething(int x)
    {
        return x *x;
    }
};

We need to change the execute function to the following (see Yakk's answer):

template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
    using zF = std::decay_t<F>&&;
    using R = std::result_of_t< zF(Args...) >;
    std::packaged_task<R()> task(
        std::bind([func = std::forward<F>(func)](auto&&...args)mutable->R{
        return std::move(func)(decltype(args)(args)...);
    }, std::forward<Args>(args)...)
        );
}

Now you can use it like that:

A a;

auto f = [](A& inst, int x) {  return inst.doSomething(x); };
execute(f, a, 5);

// or

auto g = [&]() { return a.doSomething(5); };
execute(g);

Note

Be aware of the life time of a as the packaged_task may run asynchronously.

1
votes

Here is a simple solution:

template<class...Args, class F>
void execute(F&& func)
{
  using R = std::result_of_t< std::decay_t<F>(Args...) >;
  std::packaged_task<R(Args...)> task(std::forward<F>(func));
}

I added perfect forwarding, and the option to pass in arguments. If you don't pass in arguments, it assumes the passed in callable takes 0 arguments.

Example use:

auto func = std::bind([](int x) { return x*x; }, 5);
A name;
name.execute(func);

if func requires arguments, you have to pass them explicitly. Which is only fair, as it is hard to use a std::packaged_task without knowing what arguments it will expect. ;)

This also compiles:

auto func = [](int x) { return x*x; };
A name;
name.execute<int>(func);

but how you are going to use that packaged task is tricky.

If you want to mimic the interfaces of std::async and the like, we can do:

template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
  using zF = std::decay_t<F>&&;
  using R = std::result_of_t< zF(Args...) >;
  std::packaged_task<R()> task(
    std::bind( [func=std::forward<F>(func)](auto&&...args)mutable->R{
      return std::move(func)(decltype(args)(args)...);
    }, std::forward<Args>(args)... )
  );
}

which carefully wraps the passed in func in a lambda in order to avoid calling bind on bind, and then binds the arguments passed in.

Now you can:

name.execute([](int x){ return x*x; }, 5);

without using bind at all.

The code above has not been compiled, and probably contains tpyos.