Using std::async
I was wondering whether it is possible to have a helper function, which creates std::future
s from a collection (one future for every collection element).
Often I have the following situation:
auto func = []( decltype(collection)::reference value ) {
//Some async work
};
typedef std::result_of<decltype(func)>::type ResultType;
std::vector<std::future<ResultType>> futures;
futures.reserve(collection.size());
// Create all futures
for( auto& element : collection ) {
futures.push_back(std::async(func, element));
}
// Wait till futures are done
for( auto& future : futures ) {
future.wait();
}
To be able to easily reuse this I came up with the following partial code:
template< class Function, class CT, class... Args>
std::vector<std::future<typename std::result_of<Function(Args...)>::type>>
async_all( Function&& f, CT& col ) {
typedef typename std::result_of<Function(Args...)>::type ResultType;
std::vector<std::future<ResultType>> futures;
futures.reserve(collection.size());
for( auto& element : collection ) {
futures.push_back(std::async(func, element));
}
}
return futures;
Now I have to solve the Args
problem, since in async_all
, Args
cannot be deduced anymore. The only thing I currently can think of is another functor, which converts an element in collection to Args
.
Is there any more elegant solution to this?
You're almost there. The collection passed to async_all
has all the information we need to uniquely determine the function argument type; the only question is how to extract this information. Using the auto
keyword in the function signature, we can write the return type after the function arguments. This not only produces a cleaner signature, but also lets us use the argument values themselves together with decltype
in deducing return types. For instance:
template<typename F, typename CT>
auto reduce(F f, CT coll) -> decltype(f(*begin(coll), *begin(coll));
Of course, there are other ways to determine argument types for provided functions (using function signature deduction with templates). However, these approaches can fail for cases involving overloaded functions and/or templated function objects.
The following code compiles and runs properly (prints "x=1" 10 times) under gcc 4.8 (earlier versions should work just fine). Notice how we don't even have to explicitly mention std::future
: we can use decltype directly on the std::async
statement to deduce it's type.
#include <future>
#include <vector>
#include <iostream>
template<class Function, class CT>
auto async_all(Function f, CT col)
-> std::vector<decltype(std::async(f, *std::begin(col)))>
{
std::vector<decltype(std::async(f, *std::begin(col)))> futures;
futures.reserve(col.size());
for (auto& element : col) {
futures.push_back(std::async(f, element));
}
return futures;
}
int main()
{
using namespace std;
for (auto& f : async_all([](int x) { cout << "x = " << x << endl; },
vector<int>(10, 1)))
f.get();
}
(async_all
is evaluated just once here, as the spec guarantees for range expressions in range-based for loops)