Suppose I have an asynchronous functional map primitive which takes a std::vector as input and returns a std::future to a Container of my choice as output:
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return std::async([=]
{
Container result(in.size());
for(size_t i = 0; i < in.size(); ++i)
{
result[i] = f(in[i]);
}
return result;
});
}
I'd like to build an analogous async_for_each function by adapting async_map:
template<class T, class Function>
std::future<void> async_for_each(const std::vector<T>& in, Function f);
The problem is that async_for_each returns std::future<void>, while async_map returns std::future<Container>, and void is not a Container.
I can get something close to what I want by constructing a type which fulfills the Container requirements but ignores assignments to it (empty_container in my initial attempt), but a std::future of this type is still not std::future<void>.
I have the following constraints on my solution:
- There must be only one implementation of
async_map, with the given function signature (i.e., noasync_map<void>specialization) - There must be only one
std::futurecreated (i.e., no.then()-style continuation)
I was hoping there is an efficient way to convert between std::futures of related types (or cast a std::future<T> to std::future<void>), but the answer to this question suggests it is not possible.
Random ideas:
- Can
async_for_eachwrap its function in a clever way to solve this problem? - Can the type used for
Containeract likevoidinasync_for_each, but act likeContainerinasync_map?
My initial attempt is below. Is it possible to build what I want given these constraints?
#include <future>
#include <vector>
#include <iostream>
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return std::async([=]
{
Container result(in.size());
for(size_t i = 0; i < in.size(); ++i)
{
result[i] = f(in[i]);
}
return result;
});
}
struct empty_container
{
empty_container(size_t) {}
struct empty
{
template<class T>
empty operator=(const T&) const { return empty(); }
};
empty operator[](size_t) { return empty(); }
};
template<class Function>
struct invoke_and_ignore_result
{
Function f;
template<class T>
empty_container::empty operator()(T&& x) const
{
f(std::forward<T>(x));
return empty_container::empty();
}
};
template<class T, class Function>
//std::future<void> async_for_each(const std::vector<T>& in, Function f)
std::future<empty_container> async_for_each(const std::vector<T>& in, Function f)
{
invoke_and_ignore_result<Function> g{f};
std::future<empty_container> f1 = async_map<empty_container>(in, g);
return f1;
}
int main()
{
std::vector<int> vec(5, 13);
async_for_each(vec, [](int x)
{
std::cout << x << " ";
}).wait();
std::cout << std::endl;
return 0;
}
Aucun commentaire:
Enregistrer un commentaire