How to convert std::future<T> to std::future<void>? - c++

I have a situation where I have a std::future<some_type> resulting from a call to API A, but need to supply API B with a std::future<void>:
std::future<some_type> api_a();
void api_b(std::future<void>& depend_on_this_event);
In the absence of proposed functionality such as .then() or when_all(), is there any efficient way to throw away the value attached to a std::future<T> and be left only with the underlying std::future<void> representing the event's completion?
Something like the following could work but would be potentially inefficient:
auto f = api_a();
f.wait();
auto void_f = std::async(std::launch::defer, []{});
api_b(void_f);

The best you can get is probably this:
auto f = api_a();
auto void_f = std::async(std::launch::deferred,[fut = std::move(f)]{ fut.wait();});
api_b(void_f);

template<class U>
struct convert_future_t {
template<class T>
std::future<U> operator()( std::future<T>&& f ) const {
return std::async(std::launch::deferred,
[f=std::move(f)]()->U{ return f.get(); }
);
}
}
template<>
struct convert_future_t<void> {
template<class T>
std::future<void> operator()( std::future<T>&& f ) const {
return std::async(std::launch::deferred,
[f=std::move(f)]()->void{ f.get(); }
);
}
}
template<class U, class T>
std::future<U> convert_future( std::future<T>&& f ) {
return convert_future_t<U>{}(std::move(f));
}
this is a generic version of #sbabbi's answer.
api_b( convert_future<void>( api_a() ) );
that allows for any target and dest type to work transparently.
The large downside to this approach is that the resulting future is a deferred future wrapping a (possibly async) future, which means that .wait_for() and .ready() APIs do not work like async futures do. The returned future will never be ready until waited.
So we can improve this marginally:
template<class T>
struct ready_future_t {
template<class...Us>
std::future<T> operator()( Us&&...us ) const {
std::promise<T> p;
p.set_value(T(std::forward<Us>(us)...));
return p.get_future();
}
};
template<>
struct ready_future_t<void> {
using T=void;
// throws away the Us&&...s
template<class...Us>
std::future<T> operator()( Us&&...us ) const {
std::promise<T> p;
p.set_value();
return p.get_future();
}
};
template<class T, class...Us>
std::future<T> ready_future(Us&&...us){
return ready_future_t<T>{}(std::forward<Us>(us)...);
}
template<class U>
struct convert_future_t {
template<class T>
std::future<U> operator()( std::future<T>&& f ) const {
if (f.wait_for(0ms)==std::future_status::ready)
return ready_future<U>(f.get());
return std::async(std::launch::deferred,
[f=std::move(f)]()->U{ return f.get(); }
);
}
};
template<>
struct convert_future_t<void> {
template<class T>
std::future<void> operator()( std::future<T>&& f ) const {
if (f.wait_for(0ms)==std::future_status::ready)
return ready_future<void>();
return std::async(std::launch::deferred,
[f=std::move(f)]()->void{ f.get(); }
);
}
};
where at least if the future was already ready by the time we converted it, the returned future is also ready.
live example

Related

Call lambda with other lambda result when its return type is void

The following function makes a lambda that calls the second callable with the first callable's result. If the first callable returns a tuple it will be applied to the second callable.
template<typename T>
struct is_tuple : std::false_type{};
template<typename... T>
struct is_tuple<std::tuple<T...>> : std::true_type{};
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
using source_return = decltype(source(args...));
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<decltype(args)>(args)...));
}
else
{
return target(source(std::forward<decltype(args)>(args)...));
}
};
}
However this does not compile when the source callable returns void, since it will try to call target with incomplete type void, so I tried the following:
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
using source_return = decltype(source(args...));
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<decltype(args)>(args)...));
}
else if constexpr(std::is_void_v<source_return>)
{
source(std::forward<decltype(args)>(args)...);
return target();
}
else
{
return target(source(std::forward<decltype(args)>(args)...));
}
};
}
But this doesn't seem to work somehow since it always takes the same as void branch, even when the source function cannot return void in any situation. I geuss there is something wrong with the decltype deducing the source_return. I tried to assign the result of source to a variable to decltype the that variable instead of decltype(source(args...)) but then it gives me the error that the variable is of incomplete type void in the cases it actually does return void, so I do have to check it before actually calling source.
Here is an example of pipeline usage that does not compile:
auto callable = pipeline([]{ return 10 },
[](size_t val){ return val * 10});
callable();
The reason it does not compile is because it takes the source_return is same as void branch for some reason. Anybody has any idea how I can figure out the return type of source when called with args... in a way that is more robust?
EDIT:
I got it to work by using a call_pipeline helper function. I still don't understand why this one would work and the other one doesn't though.
template<typename S, typename T, typename... Args>
constexpr decltype(auto) call_pipeline(const S& source, const T& target, Args&&... args)
{
using source_return = decltype(source(std::forward<Args>(args)...));
if constexpr(std::is_void_v<source_return>)
{
source(std::forward<Args>(args)...);
return target();
}
else
{
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<Args>(args)...));
}
else
{
return target(source(std::forward<Args>(args)...));
}
}
}
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source_init, T&& target_init)
{
return [callables = std::tuple<S, T>(std::forward<S>(source_init),
std::forward<T>(target_init))]
(auto&&... args)
{
const auto&[source, target] = callables;
return call_pipeline(source, target, std::forward<decltype(args)>(args)...);
};
}
Not sure about isn't working your way but I propose the following alternative
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source),
std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
auto tplr = [](auto s, auto && ... as)
{
using source_return
= decltype(s(std::forward<decltype(as)>(as)...));
if constexpr ( is_tuple<source_return>::value )
return s(std::forward<decltype(as)>(as)...);
else if constexpr ( std::is_void_v<source_return> )
{
s(std::forward<decltype(as)>(as)...);
return std::make_tuple();
}
else
return std::make_tuple(s(std::forward<decltype(as)>(as)...));
}(source, std::forward<decltype(args)>(args)...);
std::apply(target, tplr);
};
}
The idea is ever made target() called through std::apply with a std::tuple (maybe empty) of arguments.

Advantages/Disadvantages of Many Nested Callbacks?

I have a work distribution scheme where every unit does some book-keeping and management and pass the task to next one in order chain such as; Lets say have 3 classes: Boss, Manager, Worker
class Boss
{
void do_async(Request req, std::function<void(Result)> callback)
{
//Find eligible manager etc.
manager.do_async(Boss_request(req,...),std::bind(&Boss::callback,this,callback,std::placeholders::_1));
}
void callback(std::function<void(Result)> main_callback,Boss_result res)
{
//some book keeping
main_callback(res.main_part);
}
};
class Manager
{
void do_async(Boss_request req, std::function<void(Boss_result)> boss_callback)
{
//Find eligible worker etc. add some data to request
worker.do_async(Manager_request(req,...),std::bind(&Manager::callback,this,boss_callback,std::placeholders::_1));
}
void callback(std::function<void(Boss_result)> boss_callback,Manager_result res)
{
//some book keeping
boss_callback(res.boss_part);
}
};
class Worker
{
void do_async(Manager_request req, std::function<void(Manager_result)> manager_callback)
{
//Do job async
work_async(Worker_task(req,...),std::bind(&Worker::callback,this,manager_callback,std::placeholders::_1));
}
void callback(std::function<void(Manager_result)> manager_callback,Worker_result res)
{
//some book keeping
manager_callback(res.manager_part);
}
};
As you can see I am extensively using std::bind, std::function and std::placeholder. Does this approach has any advantages/disadvantages? if not preferable, so what is the better way to do it? Would using lambda functions be possible/preferable(as performance or code-quality) in this state?
Edit: Why do I need asynchronous access in every level, instead of only first level? Because between each classes there is a many-to-many relationship. I have couple of layers of processing units (Boss or Manager or Worker) which can order anyone in next layer. When a unit orders the job to next one in line. It must be free immediately to take new orders from above.
I haven't used directly lambda because callbacks can be a little bit large and may make it harder to read. But code-quality can be sacrificed if there is any significant performance penalty.
What you are doing here is piping data around. Embrace the pipe.
namespace chain {
template<class T, class Base=std::function<void(T)>>
struct sink:Base{
using Base::operator();
using Base::Base;
};
template<class T, class F>
sink<T> make_sink( F&& f ) {
return {std::forward<F>(f)};
}
template<class T>
using source=sink<sink<T>>;
template<class T, class F>
source<T> make_source( F&& f ) {
return {std::forward<F>(f)};
}
template<class T>
source<std::decay_t<T>> simple_source( T&& t ) {
return [t=std::forward<T>(t)]( auto&& sink ) {
return sink( t );
};
}
template<class In, class Out>
using pipe = std::function< void(source<In>, sink<Out>) >;
template<class In, class Out>
sink<In> operator|( pipe<In, Out> p, sink<Out> s ) {
return [p,s]( In in ) {
p( [&]( auto&& sink ){ sink(std::forward<In>(in)); }, s );
};
}
template<class In, class Out>
source<Out> operator|( source<In> s, pipe<Out> p ) {
return [s,p]( auto&& sink ) {
p( s, decltype(sink)(sink) );
};
}
template<class T>
std::function<void()> operator|( source<T> in, sink<T> out ) {
return [in, out]{ in(out); };
}
template<class In, class Mid, class Out>
pipe<In, Out> operator|( pipe<In, Mid> a, pipe<Mid, Out> b ) {
return [a,b]( source<In> src, sink<Out> dest ) {
b( src|a, dest );
// or a( src, b|dest );
// but I find pipe|sink -> sink to be less pleasing an implementation
};
}
}//namespace
Then write these:
pipe<Request, Result> Boss::work_pipe();
pipe<Boss_request, Boss_result> Manager::work_pipe();
pipe<Boss_request, Manager_request> Manager::process_request();
pipe<Manager_request, Manager_result> Manager::do_request();
pipe<Manager_result, Boss_results> Manager::format_result();
pipe<Manager_request, Manager_result> Worker::work_pipe();
and similar for Worker and Boss.
pipe<Request, Result> Boss::work_pipe() {
return process_request() | do_request() | format_result();
}
pipe<Boss_request, Boss_result> Manager::work_pipe() {
return process_request() | do_request() | format_result();
}
pipe<Manager_request, Manager_result> Worker::work_pipe() {
return process_request() | do_request() | format_result();
}
then:
pipe<Manager_request, Manager_result> Manager::do_request() {
return [this]( source<Manager_request> src, sink<Manager_result> dest ) {
// find worker
worker.do_request( src, dest );
};
}
pipe<Manager_output, Boss_result> Manager::format_result() {
return [this]( source<Manager_output> src, sink<Boss_result> dest ) {
src([&]( Manager_output from_worker ) {
// some book keeping
dest( from_worker.boss_part );
});
};
}
now, I made sources "sinks for sinks", because it permits a source (or a pipe) to generate 1, 0, or many messages from one invocation. I find this useful in many cases, but it does make writing pipes a bit stranger.
You can also write this in c++14 without using std::function at all, by simply applying "i am a sink" and "i am a source" and "i am a pipe" tags to lambdas (via composition, like override) then blindly hooking things up with | and hoping their type are compatible.
To do_sync, you just do this:
void Boss::do_async( Request req, sink<Result> r ) {
work_async( simple_source(req) | work_pipe() | r );
}
ie, the entire computation can be bundled up and moved around. This moves the threading work to the top.
If you need the async thread implementation to be at the bottom, you can pipe up the earlier work and pass it down.
void Boss::do_async( source<Request> req, sink<Result> r ) {
find_manager().do_async( req|process_request(), format_result()|r );
}
void Manager::do_async( source<Boss_request> req, sink<Boss_result> r ) {
find_worker().do_async( req|process_request(), format_result()|r );
}
void Worker::do_async( source<Manager_request> req, sink<Manager_result> r ) {
work_async( req|process_request()|do_request()|format_result()|r );
}
because of how the sink/source/pipes compose, you can choose what parts of the composition you pass down and which parts you pass up.
The std::function-less version:
namespace chain {
struct pipe_tag{};
struct sink_tag{};
struct source_tag{};
template<class T, class=void>
struct is_source:std::is_base_of<source_tag, T>{};
template<class T, class=void>
struct is_sink:std::is_base_of<sink_tag, T>{};
template<class T, class=void>
struct is_pipe:std::is_base_of<pipe_tag, T>{};
template<class F, class Tag>
struct tagged_func_t: F, Tag {
using F::operator();
using F::F;
tagged_func_t(F&& f):F(std::move(f)) {}
};
template<class R, class...Args, class Tag>
struct tagged_func_t<R(*)(Args...), Tag>: Tag {
using fptr = R(*)(Args...);
fptr f;
R operator()(Args...args)const{
return f( std::forward<Args>(args)... );
}
tagged_func_t(fptr fin):f(fin) {}
};
template<class Tag, class F>
tagged_func_t< std::decay_t<F>, Tag >
tag_func( F&& f ) { return {std::forward<F>(f)}; }
template<class F>
auto as_pipe( F&& f ) { return tag_func<pipe_tag>(std::forward<F>(f)); }
template<class F>
auto as_sink( F&& f ) { return tag_func<sink_tag>(std::forward<F>(f)); }
template<class F>
auto as_source( F&& f ) { return tag_func<source_tag>(std::forward<F>(f)); }
template<class T>
auto simple_source( T&& t ) {
return as_source([t=std::forward<T>(t)]( auto&& sink ) {
return sink( t );
});
}
template<class Pipe, class Sink,
std::enable_if_t< is_pipe<Pipe>{} && is_sink<Sink>{}, bool> = true
>
auto operator|( Pipe p, Sink s ) {
return as_sink([p,s]( auto&& in ) {
p( [&]( auto&& sink ){ sink(decltype(in)(in)); }, s );
});
}
template<class Source, class Pipe,
std::enable_if_t< is_pipe<Pipe>{} && is_source<Source>{}, bool> = true
>
auto operator|( Source s, Pipe p ) {
return as_source([s,p]( auto&& sink ) {
p( s, decltype(sink)(sink) );
});
}
template<class Source, class Sink,
std::enable_if_t< is_sink<Sink>{} && is_source<Source>{}, bool> = true
>
auto operator|( Source in, Sink out ) {
return [in, out]{ in(out); };
}
template<class PipeA, class PipeB,
std::enable_if_t< is_pipe<PipeA>{} && is_pipe<PipeB>{}, bool> = true
>
auto operator|( PipeA a, PipeB b ) {
return as_pipe([a,b]( auto&& src, auto&& dest ) {
b( src|a, dest );
// or a( src, b|dest );
// but I find pipe|sink -> sink to be less pleasing an implementation
});
}
template<class T>
using sink_t = tagged_func_t< std::function<void(T)>, sink_tag >;
template<class T>
using source_t = tagged_func_t< std::function<void(sink_t<T>)>, source_tag >;
template<class In, class Out>
using pipe_t = tagged_func_t< std::function<void(source_t<In>, sink_t<Out>)>, pipe_tag >;
}
which does fewer type checks, but gets rid of type erasure overhead.
The sink_t, source_t and pipe_t typedefs are useful when you need to type-erase them.
"Hello world" example using the non-type erasure version.

Trying to avoid code duplication for some specific C++ code

I have some C++ code which consists of repeats of the following pattern:
int Func(Type1 arg1, Type2 arg2)
{
RAIILock lock(Singleton::Mutex());
Instance* ptr = GetClassInstance();
if (ptr) {
return ptr->Func(arg1, arg2);
} else {
return -1;
}
}
Basically, it's trying to get a valid class instance pointer under the lock, and essentially forwarding the call from this normal function to an instance method with the same signature. The "Func" name as well as amount and type of arguments vary, but the rest of the calls are identical.
It feels like there should be some way to achieve this using templates without getting into too much template magic, but I've been unable to come up with anything.
Something like that?
template <class MEM_FUN, class... ARGS>
auto call_func(MEM_FUN&& f, ARGS&&... args)
{
RAIILock lock(Singleton::Mutex());
Instance* ptr = GetClassInstance();
if (ptr) {
return (ptr->*f)(std::forward<ARGS>(args)...);
} else {
return -1;
}
}
Calling like this:
call_func(&Instance::Func, arg1, arg2, arg3);
I would start with this primitive:
template<class F>
std::result_of_t<F(Instance*)> while_locked( F&& f ) {
RAIILock lock(Singleton::Mutex());
Instance* ptr = GetClassInstance();
if (ptr)
return std::forward<F>(f)(ptr):
else
return -1;
}
use:
int x = while_locked( [&](auto* ptr) {
ptr->foo( a, b );
} );
basically, you get the ability to do whatever you want while locked, and have access to a ptr while doing it. This lets you merge multiple method calls into one atomic operation.
This can be extended to a multi-ary while-locked, that does an ordering of mutexes on a set of ptr-sources, locks them appropriately, gets the pointers from each, then invokes your function.
It also becomes easy to write your "invoke a member function" version, but restricting your atomic operations to "only call some specific member function" seems questionable.
I will admit the syntax is nicer in C++14 than in C++11 with auto-lambda. But most major compilers have implemented auto-lambda by this point.
There is a more general pattern that looks like this:
template<class T>
struct lockable {
template<class F>
std::result_of_t<F(T const&)> read( F&& f ) const {
auto l = lock();
return std::forward<F>(f)(t);
}
template<class F>
std::result_of_t<F(T&)> write( F&& f ) {
auto l = lock();
return std::forward<F>(f)(t);
}
private:
using read_lock_t = std::shared_lock<std::shared_mutex>;
using write_lock_t = std::unique_lock<std::shared_mutex>;
read_lock_t lock() const { return read_lock_t(m); }
write_lock_t lock() { return write_lock_t(m); }
mutable std::shared_mutex m;
T t;
};
which can be augmented with operator=, copy-ctor, move-ctors, default-ctors, and multi-locking capabilities. Multi-locking comes from std::lock and creation of unlocked lockables.
read_lock_t unlocked() const { return {m, std::defer_lock}; }
write_lock_t unlocked() { return {m, std::defer_lock}; }
template<size_t...Is, class...Selfs>
friend auto lock_all( std::index_sequence<Is...>, Selfs&... selfs ) {
auto r = std::make_tuple( selfs.unlocked() );
std::lock( std::get<Is>(r)... );
return r;
}
template<class...Selfs>
friend auto lock_all( Selfs&... selfs ) {
return lock_all( std::index_sequence_for<Selfs...>{}, selfs... );
}
public:
template<class F, class...Selfs>
friend auto invoke( F&& f, Selfs&...selfs )
-> std::result_of_t< F( decltype(selfs.t)... ) >
{
auto l = lock_all(selfs...);
return std::forward<F>(f)(selfs.t...);
}
which lets you do invoke( lambda, a, b, c ) which will lock a b and c using the appropriate lock for their const-ness, and then invoke the lambda.
This allows easy writing of operator= for example:
lockable& operator=( lockable&& o ) {
if (this = std::addressof(o)) return *this;
return invoke( [](T& target, T& src){
return target = std::move(src);
}, *this, o );
}
lockable& operator=( lockable const& o ) {
if (this = std::addressof(o)) return *this;
return invoke( [](T& target, T const& src){
return target = src;
}, *this, o );
}
You might not apply too much convenience and have a guarded singleton allowing multiple member function calls:
#include <mutex>
template <typename T>
class Lockable
{
template <typename> friend class Lock;
public:
typedef std::mutex mutex_type;
typedef std::lock_guard<std::mutex> lock_guard;
public:
Lockable()
{}
template <typename A>
Lockable(A&& a)
: _value(std::forward<A>(a))
{}
template <typename A, typename ... Args>
Lockable(A&& a, Args&& ... args)
: _value(std::forward<A>(a), std::forward<Args>(args)...)
{}
Lockable(const Lockable&) = delete;
Lockable& operator = (const Lockable&) = delete;
explicit operator T () const {
lock_guard lock(_mutex);
T result = _value;
return result;
}
Lockable& operator = (const T& value) {
lock_guard lock(_mutex);
_value = value;
return *this;
}
private:
mutable mutex_type _mutex;
T _value;
};
template <typename T>
class Lock
{
public:
typedef Lockable<T> lockable_type;
typedef typename lockable_type::mutex_type mutex_type;
typedef typename lockable_type::lock_guard lock_guard;
public:
Lock(lockable_type& lockable)
: _lock(lockable._mutex), _ptr(&(lockable._value))
{}
Lock(const Lock&) = delete;
Lock& operator = (const Lock&) = delete;
operator T& () const { return *_ptr; }
T& operator * () const { return *_ptr; }
T* operator -> () const { return _ptr; }
private:
lock_guard _lock;
T* _ptr;
};
class Singleton
{
private:
friend class Lockable<Singleton>;
Singleton() {};
public:
Singleton(const Singleton&) = delete;
Singleton& operator = (const Singleton&) = delete;
static Lockable<Singleton>& instance();
int Func0(int arg) const { return 0; }
int Func1(int arg) const { return 1; }
int Func2(int arg) const { return 2; }
};
Lockable<Singleton>& Singleton::instance() {
static Lockable<Singleton> result;
return result;
}
#include <iostream>
int main() {
Lock<Singleton> singleton(Singleton::instance());
singleton->Func0(0);
singleton->Func1(1);
singleton->Func2(2);
std::cout << "Reached\n";
// Uncomment to get a deadlock
// Lock<Singleton> deadlock_singleton(Singleton::instance());
// std::cout << "Not reached\n";
return 0;
}
Note: The Lock<Singleton> singleton(Singleton::instance()); is clumsy, due to the non moveable std::lock_guard.

Adapting a function that returns std::future<T> to std::future<U>

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., no async_map<void> specialization)
There must be only one std::future created (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_each wrap its function in a clever way to solve this problem?
Can the type used for Container act like void in async_for_each, but act like Container in async_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;
}
I think you are using the wrong primitive.
Here I build everything up with a different primitive -- a sink.
A sink can consume data via operator()(T&&)&. It then returns some result via operator()()&&.
Here is a async_sink function:
template<class Container, class Sink>
std::future<std::result_of_t<std::decay_t<Sink>()>>
async_sink(Container&& c, Sink&& sink)
{
return std::async(
[c=std::forward<Container>(c), sink=std::forward<Sink>(sink)]
{
for( auto&& x : std::move(c) ) {
sink( x );
}
return std::move(sink)();
});
}
Here is an implementation of a sink that puts things into a container, then returns it:
template<class C>
struct container_sink_t {
C c;
template<class T>
void operator()( T&& t ){
c.emplace_back( std::forward<T>(t) );
}
C operator()()&&{
return std::move(c);
}
};
Here is a sink that takes a function and a sink and composes them:
template<class F, class S>
struct compose_sink_t {
F f;
S s;
template<class T>
void operator()(T&& t){
s(
f(std::forward<T>(t))
);
}
std::result_of_t<S()> operator()()&&{
return std::move(s)();
}
};
template<class C, class F>
compose_sink_t<std::decay_t<F>, container_sink_t<C>>
transform_then_container_sink( F&& f ) {
return {std::forward<F>(f)};
}
Here is a sink that takes a function, calls it, and returns void:
template<class F>
struct void_sink_t {
F f;
template<class T>
void operator()(T&& t)
{
f(std::forward<T>(t));
}
void operator()() {}
};
template<class F>
void_sink_t<std::decay_t<F>> void_sink(F&&f){return {std::forward<F>(f)}; }
now your map is:
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return async_sink(
in,
transform_then_container_sink<Container>(std::forward<F>(f))
);
}
and your for_each is:
template<class T, class Function>
std::future<void> async_for_each(const std::vector<T>& in, Function f)
{
return async_sink(
in,
void_sink(std::forward<F>(f))
);
}
I freely use C++14 features, because they made the code better. You can replace the move-into-container with a copy for a touch less efficiency, and write your own _t aliases.
The above code has not been tested or run, so there are probably bugs in it. There is one issue I'm uncertain of -- can a lambda returning void end with a return void_func() in that context? -- but as that uglyness is in one spot, it can be worked around even if it doesn't work.

Any way of doing this iterator filter with boost MPL or similar

Essentially I have a couple of situations where I'm using boost::filter_iterator to filter an iterator for some conditions. There's a situation where I want to filter for 2 conditions simultaneously and we have some pre-existing code for this already but I'd like to know if there's a idiomatic way to do this with boost or the standard library:
/*! TODO: Surely there should be something in std/boost to achieve this??? */
/*! Filter for things that satisfy F1 and F2 */
template <
typename F1,
typename F2,
typename ArgT
>
struct filter_and
{
F1 f1;
F2 f2;
filter_and(F1 _f1, F2 _f2): f1(_f1), f2(_f2)
{}
inline bool operator() (ArgT const& arg) const
{
return f1(arg) && f2(arg);
}
};
If a solution requires c++11 that should be fine as long as the latest MSVC can handle it.
Try this: make_filter_iterator( it, [=](value_type const& v) { return f1(v) && f2(v); } );
For something fancier...
bool and_in_order() { return true; }
template<typename F0, typename Funcs...>
bool and_in_order( F0&& f0, Funcs&&... funcs ) {
return f0() && and_in_order(funcs...);
}
template<typename... Funcs>
struct and_unary_functors {
std::tuple<Funcs...> funcs;
template<typename Arg, typename seq=typename make_seq<sizeof...(Funcs)>::type>
bool operator()(Arg&& arg) const;
template<typename Arg, int...s>
bool operator()<Arg, seq<s...>>(Arg&& arg) const {
return and_in_order( [&](){ return get<s>(funcs)(arg); }... );
}
};
template<typename... Funcs>
and_unary_functors<Funcs> make_and_unary( Funcs const&... funcs ) {
return {std::make_tuple(funcs...)};
};
auto filter_it = make_filter_iterator( base_iterator, make_and_unary( f1, f2, f3, f4 ) );
or something silly like that.