|
|
- #pragma once
-
- #include "core.h"
-
- #include <memory>
- #include <functional>
- #include <exception>
- #include <future>
- #include <vector>
- #include <tuple>
-
- #include "refcounted.h"
- #include "intrusive_ptr.h"
- #include "Try.h"
- #include "function_traits.h"
-
- namespace vlc
- {
- template<typename T> class future;
- template<typename T> class promise;
-
- namespace detail
- {
- template<typename T> class FutureObject : public Refcounted<FutureObject<T>>
- {
- enum class State : int32_t
- {
- Start,
- ResultSet,
- CallbackSet,
- Finished
- };
-
- public:
- using Function = std::function<void(Try<T>&&)>;
-
- FutureObject() : m_state(State::Start)
- {
- }
-
- explicit FutureObject(Try<T>&& value) :
- m_state(State::ResultSet),
- m_result(std::move(value))
- {
-
- }
-
- inline State state() const
- {
- return m_state.load(std::memory_order_acquire);
- }
-
- bool ready() const
- {
- switch (state())
- {
- case State::ResultSet:
- case State::Finished:
- return true;
- default:
- return false;
- }
- }
-
- Try<T>& result()
- {
- if (ready())
- {
- return m_result;
- }
-
- throw std::logic_error("Future is not ready");
- }
-
- template<typename F> void setCallback(F&& f)
- {
- m_callback = std::forward<F>(f);
-
- for (;;)
- {
- auto old_state = state();
-
- switch (old_state)
- {
- case State::Start:
- if (m_state.compare_exchange_weak(old_state, State::CallbackSet, std::memory_order_release, std::memory_order_acquire))
- return;
-
- // State has changed. Reload value and try again
- break;
-
- case State::ResultSet:
- m_state.store(State::Finished, std::memory_order_relaxed);
- fireAndRelease();
- return;
-
- default:
- throw std::logic_error("Invalid state");
- }
- }
- }
-
- void setResult(Try<T>&& value)
- {
- m_result = std::move(value);
-
- for (;;)
- {
- auto old_state = state();
-
- switch (old_state)
- {
- case State::Start:
- m_lock.lock();
- if (m_state.compare_exchange_weak(old_state, State::ResultSet, std::memory_order_release, std::memory_order_acquire))
- {
- m_lock.unlock();
- cv.notify_all();
- return;
- }
- m_lock.unlock();
-
- // State has changed. Reload value and try again
- break;
-
- case State::CallbackSet:
- m_state.store(State::Finished, std::memory_order_relaxed);
- fireAndRelease();
- return;
-
- default:
- throw std::logic_error("Invalid state");
- }
- }
- }
-
- void wait() const
- {
- std::unique_lock<std::mutex> lk(m_lock);
-
- assert(state() == State::Start || state() == State::ResultSet);
- cv.wait(lk, [this]() { return state() == State::ResultSet; });
- assert(state() == State::ResultSet);
- }
-
- template<class Rep, class Period>
- bool wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const
- {
- std::unique_lock<std::mutex> lk(m_lock);
-
- assert(state() == State::Start || state() == State::ResultSet);
- bool status = cv.wait_for(lk, timeout_duration, [this]() { return state() == State::ResultSet; });
- assert(status || !(state() == State::ResultSet));
-
- return status;
- }
-
- template<class Clock, class Duration>
- bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const
- {
- std::unique_lock<std::mutex> lk(m_lock);
-
- assert(state() == State::Start || state() == State::ResultSet);
- bool status = cv.wait_until(lk, timeout_time, [this]() { return state() == State::ResultSet; });
-
- return status;
- }
-
- private:
- void fireAndRelease()
- {
- assert(m_callback);
- assert(m_state == State::Finished);
-
- m_callback(std::move(m_result));
- m_callback = nullptr;
- }
-
- std::atomic<State> m_state;
-
- Function m_callback;
- Try<T> m_result;
-
- mutable std::condition_variable cv;
- mutable std::mutex m_lock;
- };
-
- template<typename T> struct IsFuture : std::false_type
- {
- using type = T;
- };
-
- template<typename T> struct IsFuture<future<T>> : std::true_type
- {
- using type = T;
- };
-
- template<typename F, typename... Args>
- using result_of = decltype(std::declval<F>()(std::declval<Args>()...));
-
- template<bool IsTry, typename F, typename... Args>
- struct ArgResult
- {
- using Result = result_of<F, Args...>;
- };
-
- template<typename F, typename... Args>
- struct IsCallableWith
- {
- template<typename T, typename = result_of<T, Args...>>
- static constexpr std::true_type check(std::nullptr_t);
-
- template<typename>
- static constexpr std::false_type check(...);
-
- static constexpr bool value = decltype(check<F>(nullptr))::value;
- };
-
- template<typename T, typename F>
- struct CallableResult
- {
- using Argument =
- typename std::conditional<
- IsCallableWith<F>::value, ArgResult<false, F>,
- typename std::conditional<
- IsCallableWith<F, T&&>::value, ArgResult<false, F, T&&>,
- typename std::conditional<
- IsCallableWith<F, T&>::value, ArgResult<false, F, T&>,
- typename std::conditional<
- IsCallableWith<F, Try<T>&&>::value, ArgResult<true, F, Try<T>&&>,
- ArgResult<true, F, Try<T>&>
- >::type
- >::type
- >::type
- >::type;
-
- using ReturnsFuture = IsFuture<typename Argument::Result>;
- using Result = future<typename ReturnsFuture::type>;
- };
-
- template<typename F>
- struct CallableResult<void, F>
- {
- using Argument =
- typename std::conditional <
- IsCallableWith<F>::value, ArgResult<false, F>,
- typename std::conditional<
- IsCallableWith<F, Try<void>&&>::value, ArgResult<true, F, Try<void>&&>,
- ArgResult<true, F, Try<void>&>
- >::type
- >::type;
-
- using ReturnsFuture = IsFuture<typename Argument::Result>;
- using Result = future<typename ReturnsFuture::type>;
- };
-
- template<typename T> struct FunctionReferenceToPointer
- {
- using type = T;
- };
-
- template<typename R, typename... Args> struct FunctionReferenceToPointer<R(&)(Args...)>
- {
- using type = R(*)(Args...);
- };
-
- template<typename... Args> struct Passables : public Refcounted<Passables<Args...>>
- {
- using TupleType = std::tuple<typename std::decay<Args>::type...>;
- TupleType t;
-
- Passables(std::tuple<Args...>&& v) : t(std::move(v))
- {
-
- }
-
- template<typename... SourceArgs> Passables(SourceArgs&&... args) : t(std::forward<SourceArgs>(args)...)
- {
- }
-
- Passables(Passables&& src)
- {
- std::swap(t, src.t);
- }
-
- Passables& operator = (Passables&& src)
- {
- std::swap(t, src.t);
-
- return *this;
- }
-
- template<size_t idx> auto arg() -> typename std::tuple_element<idx, TupleType>::type &
- {
- return std::get<idx>(t);
- }
-
- template<size_t idx> auto arg() const -> const typename std::tuple_element<idx, TupleType>::type&
- {
- return std::get<idx>(t);
- }
-
- Passables(const Passables&) = delete;
- Passables& operator = (const Passables&) = delete;
- };
- }
-
- template<typename T> class future
- {
- public:
- using value_type = T;
-
- future() noexcept = default;
- ~future() = default;
-
- future(const future&) = delete;
- future& operator = (const future&) = delete;
-
- future(future&& src) noexcept : m_obj(std::move(src.m_obj))
- {
- }
-
- future& operator = (future&& src) noexcept
- {
- std::swap(m_obj, src.m_obj);
- return *this;
- }
-
- template<typename ValueType = T, typename =
- typename std::enable_if<!detail::IsFuture<typename std::decay<ValueType>::type>::value>::type
- >
-
- future(ValueType&& value) :
- m_obj(new detail::FutureObject<T>(Try<T>(std::forward<ValueType>(value))))
- {
-
- }
-
- bool valid() const noexcept
- {
- return (bool)m_obj;
- }
-
- bool ready() const
- {
- return valid() ? m_obj->ready() : false;
- }
-
- future& wait()
- {
- if (ready())
- {
- return *this;
- }
-
- throwIfInvalid();
-
- m_obj->wait();
-
- return *this;
- }
-
- template<class Rep, class Period>
- bool wait_for(const std::chrono::duration<Rep, Period>& timeout_time) const
- {
- if (ready())
- {
- return true;
- }
-
- throwIfInvalid();
-
- return m_obj->wait_for(timeout_time);
- }
-
- template<class Clock, class Duration>
- bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const
- {
- if (ready())
- {
- return true;
- }
-
- throwIfInvalid();
-
- return m_obj->wait_until(timeout_time);
- }
-
- T get()
- {
- return std::move(wait().value());
- }
-
- typename std::add_lvalue_reference<T>::type
- value()
- {
- throwIfInvalid();
-
- return m_obj->result().value();
- }
-
- typename std::add_lvalue_reference<const T>::type
- value() const
- {
- throwIfInvalid();
-
- return m_obj->result().value();
- }
-
- template<typename F,
- typename FP = typename detail::FunctionReferenceToPointer<F>::type,
- typename R = detail::CallableResult<T, FP>>
- typename R::Result then(F&& func)
- {
- using Arguments = typename R::Argument;
-
- return then_impl<FP, R>(std::forward<FP>(func), Arguments());
- }
-
- template<typename F> future<T> on_exception(F&& func)
- {
- return on_error_impl(std::forward<F>(func));
- }
-
- template<typename F> future<T> ensure(F&& func)
- {
- auto pt = make_intrusive<detail::Passables<F>>(std::forward<F>(func));
-
- return then([pt](Try<T>&& result)
- {
- std::move(pt->template arg<0>())();
- return make_future(std::move(result));
- });
- }
-
- /// Internal methods
- using FutureObjectPtr = IntrusivePtr<detail::FutureObject<T>>;
-
- template<typename F> void setCallback(F&& func)
- {
- throwIfInvalid();
-
- m_obj->setCallback(std::forward<F>(func));
- }
-
- private:
- FutureObjectPtr m_obj;
-
- template<typename U> friend class promise;
- template<typename U> friend future<U> make_future(Try<U>&&);
-
- explicit future(FutureObjectPtr obj) : m_obj(obj)
- {
- }
-
- void throwIfInvalid() const
- {
- if (!m_obj)
- {
- throw std::future_error(std::future_errc::no_state);
- }
- }
-
- template<typename F, typename R, bool isTry, typename... Args>
- typename std::enable_if<!R::ReturnsFuture::value, typename R::Result>::type
- // If returns T
- then_impl(F&& func, detail::ArgResult<isTry, F, Args...>)
- {
- static_assert(sizeof...(Args) <= 1, "then callback must accept one or zero arguments");
-
- throwIfInvalid();
-
- using RetType = typename R::ReturnsFuture::type;
-
- promise<RetType> p;
-
- auto f = p.get_future();
-
- auto pt = make_intrusive<detail::Passables<promise<RetType>, F>>(std::move(p), std::forward<F>(func));
-
- setCallback([pt](Try<T>&& result) mutable
- {
- auto&& pm = pt->template arg<0>();
- auto&& func = pt->template arg<1>();
-
- if (!isTry && result.isFailure())
- {
- pm.set_exception(std::move(result.exception()));
- }
- else
- {
- pm.set_with([&]
- {
- return std::move(func)(result.template get<isTry, Args>()...);
- });
- }
- });
-
- return f;
- }
-
- template<typename F, typename R, bool isTry, typename... Args>
- typename std::enable_if<R::ReturnsFuture::value, typename R::Result>::type
- // If returns future<T>
- then_impl(F&& func, detail::ArgResult<isTry, F, Args...>)
- {
- static_assert(sizeof...(Args) <= 1, "then callback must accept one or zero arguments");
-
- throwIfInvalid();
-
- using RetType = typename R::ReturnsFuture::type;
-
- promise<RetType> p;
-
- auto f = p.get_future();
-
- auto pt = make_intrusive<detail::Passables<promise<RetType>, F>>(std::move(p), std::forward<F>(func));
-
- setCallback([pt](Try<T>&& result) mutable
- {
- auto&& pm = pt->template arg<0>();
- auto&& func = pt->template arg<1>();
-
- std::exception_ptr exc;
-
- if (result.isFailure())
- {
- exc = std::move(result.exception());
- }
- else
- {
- try
- {
- auto f2 = std::move(func)(result.template get<isTry, Args>()...);
-
- f2.setCallback([pt](Try<RetType>&& result2)
- {
- pt->template arg<0>().set_try(std::move(result2));
- });
- }
- catch (...)
- {
- exc = std::current_exception();
- }
- }
-
- if (exc)
- {
- pm.set_exception(exc);
- }
- });
-
- return f;
- }
-
- template<typename F, typename R = typename function_traits<F>::result_type>
- // If callback returns T
- typename std::enable_if<!detail::IsFuture<R>::value, future<T>>::type on_error_impl(F&& func)
- {
- using Exc = typename function_traits<F>::template arg<0>::type;
- static_assert(std::is_same<R, T>::value, "Return type of on_error callback must be T or future<T>");
-
- promise<T> p;
- auto f = p.get_future();
-
- auto pt = make_intrusive<detail::Passables<promise<T>, F>>(std::move(p), std::forward<F>(func));
-
- setCallback([pt](Try<T>&& result) mutable
- {
- if (!result.template recoverWith<Exc>([&](Exc& e)
- {
- auto&& pm = pt->template arg<0>();
- auto&& func = pt->template arg<1>();
-
- pm.set_with([&]
- {
- return std::move(func)(e);
- });
- }))
- {
- pt->template arg<0>().set_try(std::move(result));
- }
- });
-
- return f;
- }
-
- template<typename F, typename R = typename function_traits<F>::result_type>
- // If callback returns future<T>
- typename std::enable_if<detail::IsFuture<R>::value, future<T>>::type on_error_impl(F&& func)
- {
- using Exc = typename function_traits<F>::template arg<0>::type;
- static_assert(std::is_same<R, future<T>>::value, "Return type of on_error callback must be T or future<T>");
-
- promise<T> p;
- auto f = p.get_future();
-
- auto pt = make_intrusive<detail::Passables<promise<T>, F>>(std::move(p), std::forward<F>(func));
-
- setCallback([pt](Try<T>&& result)
- {
- auto&& pm = pt->template arg<0>();
-
- if (!result.template recoverWith<Exc>([&](Exc& e)
- {
- auto&& f = pt->template arg<1>();
-
- std::exception_ptr exc;
-
- try
- {
- auto f2 = std::move(f)(e);
-
- f2.setCallback([pt](Try<T>&& result2) mutable
- {
- auto&& pm = pt->template arg<0>();
-
- pm.set_try(std::move(result2));
- });
- }
- catch (...)
- {
- exc = std::current_exception();
- }
-
- if (exc)
- {
- pm.set_exception(std::move(exc));
- }
- }))
- {
- auto&& pm = pt->template arg<0>();
-
- pm.set_try(std::move(result));
- }
- });
-
- return f;
- }
- };
-
- template<typename T> class promise
- {
- public:
- promise() : m_obj(new detail::FutureObject<T>())
- {
- }
-
- ~promise()
- {
- if (m_obj)
- {
- if (m_future_retrieved && !m_obj->ready())
- {
- m_obj->setResult(Try<T>(std::make_exception_ptr(std::future_error(std::future_errc::broken_promise))));
- }
- }
- }
-
- promise(const promise&) = delete;
- promise& operator = (const promise&) = delete;
-
- promise(promise&& src) noexcept :
- m_obj(std::move(src.m_obj)),
- m_future_retrieved(src.m_future_retrieved)
- {
- src.m_future_retrieved = false;
- }
-
- promise& operator = (promise&& src) noexcept
- {
- std::swap(m_obj, src.m_obj);
- std::swap(m_future_retrieved, src.m_future_retrieved);
-
- return *this;
- }
-
- future<T> get_future()
- {
- throwIfRetrieved();
-
- m_future_retrieved = true;
-
- return future<T>(m_obj);
- }
-
- void set_try(Try<T>&& t)
- {
- throwIfFulfilled();
-
- m_obj->setResult(std::move(t));
- }
-
- template<typename ValueType> void set_value(const ValueType& value)
- {
- throwIfFulfilled();
-
- m_obj->setResult(Try<T>(value));
- }
-
- template<typename ValueType> void set_value(ValueType&& value)
- {
- throwIfFulfilled();
-
- m_obj->setResult(Try<T>(std::forward<ValueType>(value)));
- }
-
- void set_exception(std::exception_ptr exc)
- {
- throwIfFulfilled();
-
- m_obj->setResult(Try<T>(exc));
- }
-
- template<typename F> void set_with(F&& setter)
- {
- throwIfFulfilled();
-
- m_obj->setResult(make_try_with(std::forward<F>(setter)));
- }
-
- private:
- using FutureObjectPtr = IntrusivePtr<detail::FutureObject<T>>;
-
- FutureObjectPtr m_obj;
- bool m_future_retrieved = false;
-
- void throwIfRetrieved()
- {
- if (m_future_retrieved)
- {
- throw std::future_error(std::future_errc::future_already_retrieved);
- }
- }
-
- void throwIfFulfilled()
- {
- if (!m_obj)
- {
- throw std::future_error(std::future_errc::no_state);
- }
-
- if (m_obj->ready())
- {
- throw std::future_error(std::future_errc::promise_already_satisfied);
- }
- }
- };
-
- /// Helpers
- template<typename T> future<T> make_future(Try<T>&& t)
- {
- return future<T>(make_intrusive<detail::FutureObject<T>>(std::move(t)));
- }
-
- template<typename T>
- future<typename std::decay<T>::type> make_future(T&& value)
- {
- using DecayedType = typename std::decay<T>::type;
-
- return make_future(Try<DecayedType>(std::forward<T>(value)));
- }
-
- template<typename T> future<T> make_future(const std::exception_ptr& e)
- {
- return make_future(Try<T>(e));
- }
-
- template<typename T, typename E>
- typename std::enable_if<std::is_base_of<std::exception, E>::value, future<T>>::type
- make_future(const E& e)
- {
- return make_future(std::make_exception_ptr(e));
- }
-
- template<typename F, typename R = typename function_traits<F>::result_type>
- typename std::enable_if<detail::IsFuture<R>::value, R>::type
- make_future_with(F&& func)
- {
- using T = typename detail::IsFuture<R>::type;
-
- try
- {
- return func();
- }
- catch (...)
- {
- return make_future<T>(std::current_exception());
- }
- }
-
- template<typename F, typename R = typename function_traits<F>::result_type>
- typename std::enable_if<!detail::IsFuture<R>::value, future<R>>::type
- make_future_with(F&& func)
- {
- return make_future<R>(make_try_with([&func]() mutable
- {
- return func();
- }));
- }
-
- inline future<void> make_future()
- {
- return future<void>();
- }
-
- template<typename Iterator, typename F,
- typename IteratorValueType = typename std::iterator_traits<Iterator>::value_type,
- typename ResultType = typename decltype(std::declval<IteratorValueType>().then(std::declval<F>()))::value_type
- >
- auto map(Iterator first, Iterator last, F&& func) -> std::vector<future<ResultType>>
- {
- std::vector<future<ResultType>> result(std::distance(first, last));
-
- for (size_t id = 0; first != last; ++first, ++id)
- {
- result[id] = first->then(std::forward<F>(func));
- }
-
- return result;
- }
-
- template<typename Container, typename F>
- auto map(Container&& c, F&& f) -> decltype(map(c.begin(), c.end(), f))
- {
- return map(c.begin(), c.end(), std::forward<F>(f));
- }
-
- namespace detail
- {
- template<typename T> struct Collector
- {
- using ResultType = std::vector<T>;
-
- explicit Collector(size_t n) : result(n), has_exception(false)
- {
-
- }
-
- ~Collector()
- {
- if (!has_exception)
- {
- p.set_value(std::move(result));
- }
- }
-
- void setResult(uint32_t i, Try<T>& t)
- {
- result[i] = std::move(t.value());
- }
-
- promise<ResultType> p;
- ResultType result;
- std::atomic<bool> has_exception;
- };
-
- template<> struct Collector<void>
- {
- using ResultType = void;
-
- explicit Collector(size_t) : has_exception(false)
- {
-
- }
-
- ~Collector()
- {
- if (!has_exception)
- {
- p.set_value(Try<void>());
- }
- }
-
- void setResult(uint32_t, Try<void>&)
- {
- }
-
- promise<void> p;
- std::atomic<bool> has_exception;
- };
-
- template<typename ValueType> struct CollectorAll
- {
- using ResultType = std::vector<Try<ValueType>>;
-
- CollectorAll(size_t n) : results(n)
- {
-
- }
-
- ~CollectorAll()
- {
- p.set_value(std::move(results));
- }
-
- void setResult(uint32_t i, Try<ValueType>&& t)
- {
- results[i] = std::forward<Try<ValueType>>(t);
- }
-
- promise<ResultType> p;
- ResultType results;
- };
-
- template<> struct CollectorAll<void>
- {
- using ResultType = void;
-
- CollectorAll(size_t)
- {
-
- }
-
- ~CollectorAll()
- {
- p.set_value(Try<ResultType>());
- }
-
- void setResult(uint32_t, Try<ResultType>&&)
- {
- }
-
- promise<ResultType> p;
- };
-
- template<typename Iterator> using TypeFromIterator =
- typename std::iterator_traits<Iterator>::value_type;
- }
-
- /// Like collectAll, but will short circuit on the first exception. Thus, the
- /// type of the returned Future is std::vector<T> instead of
- /// std::vector<Try<T>>
- template<typename Iterator> auto collect(Iterator first, Iterator last) ->
- future<typename detail::Collector<typename detail::TypeFromIterator<Iterator>::value_type>::ResultType>
- {
- using ValueType = typename detail::TypeFromIterator<Iterator>::value_type;
-
- auto ctx = std::make_shared<detail::Collector<ValueType>>(std::distance(first, last));
-
- uint32_t id = 0;
- for (; first != last; ++first, ++id)
- {
- first->setCallback([id, ctx](Try<ValueType>&& t)
- {
- if (t.isFailure())
- {
- if (!ctx->has_exception.exchange(true))
- {
- ctx->p.set_exception(std::move(t.exception()));
- }
- }
- else if (!ctx->has_exception)
- {
- ctx->setResult(id, t);
- }
- });
- }
-
- return ctx->p.get_future();
- }
-
- template<typename Container> auto collect(Container&& c) -> decltype(collect(c.begin(), c.end()))
- {
- return collect(c.begin(), c.end());
- }
-
- /**
- When all the input Futures complete, the returned Future will complete.
- Errors do not cause early termination; this Future will always succeed
- after all its Futures have finished (whether successfully or with an
- error).
- The Futures are moved in, so your copies are invalid. If you need to
- chain further from these Futures, use the variant with an output iterator.
- */
- template<typename Iterator> auto collectAll(Iterator first, Iterator last) ->
- future<typename detail::CollectorAll<typename detail::TypeFromIterator<Iterator>::value_type>::ResultType>
- {
- using ValueType = typename detail::TypeFromIterator<Iterator>::value_type;
-
- auto ctx = std::make_shared<detail::CollectorAll<ValueType>>(std::distance(first, last));
-
- uint32_t id = 0;
- for (; first != last; ++first, ++id)
- {
- first->setCallback([id, ctx](Try<ValueType>&& t)
- {
- ctx->setResult(id, std::forward<Try<ValueType>>(t));
- });
- }
-
- return ctx->p.get_future();
- }
-
- template<typename Container> auto collectAll(Container&& c) -> decltype(collectAll(c.begin(), c.end()))
- {
- return collectAll(c.begin(), c.end());
- }
-
- namespace detail
- {
- template<typename... Ts> struct CollectorVariadic
- {
- promise<std::tuple<Ts...>> p;
- std::tuple<Try<Ts>...> result;
- std::atomic<bool> has_exception;
-
- using ResultType = future<std::tuple<Ts...>>;
-
- CollectorVariadic() : has_exception(false)
- {
-
- }
-
- ~CollectorVariadic()
- {
- if (!has_exception)
- {
- p.set_value(unwrap_tuple(std::move(result)));
- }
- }
-
- CollectorVariadic(const CollectorVariadic&) = delete;
- CollectorVariadic& operator = (const CollectorVariadic&) = delete;
-
- template<size_t i, typename T> void set_result(Try<T>& t)
- {
- if (t.isFailure())
- {
- if (!has_exception.exchange(true))
- {
- p.set_exception(t.exception());
- }
- }
- else if (!has_exception)
- {
- std::get<i>(result) = std::move(t);
- }
- }
- };
-
- template<typename... Ts> struct CollectorAllVariadic
- {
- promise<std::tuple<Try<Ts>...>> p;
- std::tuple<Try<Ts>...> result;
-
- using ResultType = future<std::tuple<Try<Ts>...>>;
-
- CollectorAllVariadic() = default;
-
- ~CollectorAllVariadic()
- {
- p.set_value(std::move(result));
- }
-
- template<size_t i, typename T> void set_result(Try<T>& t)
- {
- std::get<i>(result) = std::move(t);
- }
- };
-
- template< template<typename...> class Ctx, typename... Ts> void setCallbackCollectorVariadic(const std::shared_ptr<Ctx<Ts...>>&)
- {
-
- }
-
- template< template<typename...> class Ctx, typename... Ts, typename Head, typename... Rest>
- void setCallbackCollectorVariadic(const std::shared_ptr<Ctx<Ts...>>& ctx, Head&& head, Rest&&... rest)
- {
- head.setCallback([ctx](Try<typename Head::value_type>&& t)
- {
- ctx->template set_result<sizeof...(Ts)-sizeof...(Rest)-1>(t);
- });
-
- setCallbackCollectorVariadic(ctx, std::forward<Rest>(rest)...);
- }
- }
-
- template<typename... Futures> auto collect(Futures&&... fs) -> typename detail::CollectorVariadic<typename std::decay<Futures>::type::value_type...>::ResultType
- {
- auto ctx = std::make_shared<detail::CollectorVariadic<typename std::decay<Futures>::type::value_type...>>();
-
- detail::setCallbackCollectorVariadic(ctx, std::forward<typename std::decay<Futures>::type>(fs)...);
-
- return ctx->p.get_future();
- }
-
- template<typename... Futures> auto collectAll(Futures&&... fs) -> typename detail::CollectorAllVariadic<typename std::decay<Futures>::type::value_type...>::ResultType
- {
- auto ctx = std::make_shared<detail::CollectorAllVariadic<typename std::decay<Futures>::type::value_type...>>();
-
- detail::setCallbackCollectorVariadic(ctx, std::forward<typename std::decay<Futures>::type>(fs)...);
-
- return ctx->p.get_future();
- }
- }
|