Reputation: 25
I'm trying to make a class that consume functions by istantiating asyncs. Thats my code:
main.cpp
MPIAsyncPool pool;
MPIFuture val = pool.addTask(launch::async, [](int a) -> int {
return a;
}, 7);
cout << "Value: " << val.get() << endl;
MPIFuture v = pool.addTask(std::launch::async, doThings, 46);
cout << "Value: " << v.get() << endl;
MPIAsyncPool(.h&.cpp)
class MPIAsyncPool {
public:
MPIAsyncPool();
template<typename T, typename U>
MPIFuture addTask(std::launch, std::function<T(U...)> f, U...);
};
template<typename ReturnType, typename ArgsTypes>
MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function<ReturnType(ArgsTypes...)> f, ArgsTypes... args) {
std::future<int> fut = std::async(launch, f, args);
return MPIFuture(fut.get());
}
It seems that the declaration is ok, but I've some trouble with the definition signature. Am I wrong?
Those are the errors that I get:
/home/quero/ClionProjects/MPIAsyncPool/main.cpp: In function ‘int main(int, char**)’: /home/quero/ClionProjects/MPIAsyncPool/main.cpp:17:15: error: no matching function for call to ‘MPIFuture::MPIFuture()’ MPIFuture val; ^ In file included from /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:11:0, from /home/quero/ClionProjects/MPIAsyncPool/main.cpp:3: /home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:12:5: note: candidate: MPIFuture::MPIFuture(int) MPIFuture(int); ^ /home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:12:5: note: candidate expects 1 argument, 0 provided /home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note: candidate: constexpr MPIFuture::MPIFuture(const MPIFuture&) class MPIFuture { ^ /home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note: candidate expects 1 argument, 0 provided /home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note: candidate: constexpr MPIFuture::MPIFuture(MPIFuture&&) /home/quero/ClionProjects/MPIAsyncPool/MPIfuture.h:10:7: note: candidate expects 1 argument, 0 provided /home/quero/ClionProjects/MPIAsyncPool/main.cpp:20:13: error: no matching function for call to ‘MPIAsyncPool::addTask(std::launch, main(int, char**)::, int)’ }, 7); ^ In file included from /home/quero/ClionProjects/MPIAsyncPool/main.cpp:3:0: /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note: candidate: template MPIFuture MPIAsyncPool::addTask(std::launch, std::function, U, ...) MPIFuture addTask(std::launch, std::function f, U...); ^ /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note: template argument deduction/substitution failed: /home/quero/ClionProjects/MPIAsyncPool/main.cpp:20:13: note: ‘main(int, char**)::’ is not derived from ‘std::function’ }, 7); ^ /home/quero/ClionProjects/MPIAsyncPool/main.cpp:22:64: error: no matching function for call to ‘MPIAsyncPool::addTask(std::launch, int (&)(int), int)’ MPIFuture v = pool.addTask(std::launch::async, doThings, 46); ^ In file included from /home/quero/ClionProjects/MPIAsyncPool/main.cpp:3:0: /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note: candidate: template MPIFuture MPIAsyncPool::addTask(std::launch, std::function, U, ...) MPIFuture addTask(std::launch, std::function f, U...); ^ /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: note: template argument deduction/substitution failed: /home/quero/ClionProjects/MPIAsyncPool/main.cpp:22:64: note: mismatched types ‘std::function’ and ‘int (*)(int)’ MPIFuture v = pool.addTask(std::launch::async, doThings, 46); ^ /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.cpp:14:109: error: expansion pattern ‘ArgsTypes’ contains no argument packs MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function f, ArgsTypes... args) { ^ /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.cpp:14:11: error: prototype for ‘MPIFuture MPIAsyncPool::addTask(std::launch, std::function)’ does not match any in class ‘MPIAsyncPool’ MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function f, ArgsTypes... args) { ^ In file included from /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.cpp:5:0: /home/quero/ClionProjects/MPIAsyncPool/MPIAsyncPool.h:19:15: error: candidate is: template MPIFuture MPIAsyncPool::addTask(std::launch, std::function, U, ...) MPIFuture addTask(std::launch, std::function f, U...);
Upvotes: 1
Views: 1862
Reputation: 26476
You didn't post the exact error, but I'm guessing it's because you don't use variadic templates, like you should. The addTask
signature should be look like:
template<typename T, typename... U>
MPIFuture addTask(std::launch, std::function<T(U...)> f, U...);
Also, you must expand args in the function implementation:
std::future<int> fut = std::async(launch, f, args...);
Plus, I would move the arguments around instead of passing them as values. The overall implementation should look like:
template<typename ReturnType, typename... ArgsTypes>
MPIFuture MPIAsyncPool::addTask(std::launch launch, const std::function<ReturnType(ArgsTypes...)>& f, ArgsTypes&&... args) {
std::future<int> fut = std::async(launch, f, std::forward<Args>(args)...);
return MPIFuture(fut);
}
Upvotes: 0
Reputation: 275230
std::function
is a type whose purpose is type erasure, allowing you to store more than one type of invokable value in the same type of storage. You are not doing this.
Template type deduction is what happens when you deduce template parameters from the arguments to a function call. You are doing this.
Type erasure, and type deduction, are inverses. Your code tries to deduce what type to erase to, which doesn't work both practically and rarely makes sense in theory.
And, type erasure also happens within std::async
: each layer of type erasure has a cost (run time and compile time), so only use it when you need to store information about more than one type in the same variable.
Here is a first pass on your code:
class MPIAsyncPool {
public:
MPIAsyncPool();
// R is the return type of F when invoked with Us...
template<class F, class...Us, class R=typename std::result_of<F&(Us...)>::type>
MPIFuture addTask(std::launch, F&& f, Us&&...);
};
also in the .h file:
template<class F, class...Us, class R>
MPIFuture MPIAsyncPool::addTask(std::launch launch, F&& f, Us...us) {
std::future<R> fut = std::async(launch, std::forward<F>(f), std::forward<Us>(us)...);
return MPIFuture(fut.get());
}
now, this still sucks, because when you return
you block on the async. And the entire point of async is not to block until you need it.
Change:
return MPIFuture(fut.get());
to
return MPIFuture(std::move(fut));
this may require fixing MPIFuture
, but that is the signature that makes sense. You want to take std::future
's state, not block and get its value.
Finally, you cannot define templates in .cpp files and use them outside that .cpp file. Here is a use for type erasure!
If you really want to put the implementation inside a .cpp
file, and are willing to restrict your code to code that returns int
, we can do this:
class MPIAsyncPool {
public:
MPIAsyncPool();
template<class F, class...Us, class R=typename std::result_of<F&(Us...)>::type>
MPIFuture addTask(std::launch l, F f, Us...us) {
// does not support move-only f or us...:
return addTaskInternal(l, [=]()->int{
return std::move(f)(std::move(us)...);
});
}
private:
MPIFuture addTaskInternal(std::launch, std::function<int()>);
};
in .cpp:
MPIFuture MPIAsyncPool::addTask(std::launch launch, std::function<int()> f) {
std::future<int> fut = std::async(launch, std::move(f));
return MPIFuture(std::move(fut));
}
now the header file forwards to the internal method, which uses a non-template signature. We type-erase the lambda so that all we remember about it is how to invoke it, destroy it and copy it. This erasure makes it possible to turn it into a "normal" method (not a template method).
Upvotes: 1