Last active
April 26, 2018 15:53
-
-
Save RedBeard0531/f2466679b3acd06cd79d06e5cf29fda6 to your computer and use it in GitHub Desktop.
Future then() traits
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <tuple> | |
#include <optional> | |
#include <functional> | |
///////////////////// | |
/// PRIMARY TEMPLATES | |
template <typename T> | |
struct future_then_return_traits { | |
// Future::then(cb) for a callback that returns T, | |
// returns Future<future_then_return_traits<T>::result_type> | |
using result_type = T; | |
// This is called to complete the output future after a then() callback returns T. | |
// result is the return value from the callback. | |
// promise is associated with the returned Future<result_type<T>. | |
// We can easily replace the promise with some other completion API. | |
template <typename Promise> | |
static void adapt(Promise&& p, T&& result) { | |
p.set_value(std::move(result)); | |
} | |
}; | |
template <typename T> | |
struct future_invocation_traits { | |
// This is called when a Future<T> is ready to invoke a callback. | |
// It allows a type to customize how it is passed to the callback. | |
// result is a ready future. | |
template <typename Callback, typename Future> | |
static auto invoke(Callback&& cb, Future&& result) { | |
// Also need error propagation. Exercise for reader. | |
return std::invoke(std::forward<Callback>(cb), std::move(result).get()); | |
} | |
}; | |
////////////////////////////////////////////////////////////// | |
/// Specializations for a concrete future type with unwrapping | |
template <typename T> | |
struct future_t; | |
template <typename T> | |
struct future_then_return_traits<future_t<T>> { | |
using result_type = T; | |
template <typename Promise> | |
static void adapt(Promise&& p, future_t<T>&& result) { | |
// Also need error propagation. Exercise for reader. | |
std::move(result).then([p = std::forward<Promise>(p)] (T&& v) { | |
p.set_value(std::move(v)); | |
}); | |
} | |
}; | |
// Uses default invocation traits. | |
/////////////////////////////// | |
/// Specializations for tuple | |
// Uses default return traits. | |
template <typename... Ts> | |
struct future_invocation_traits<std::tuple<Ts...>> { | |
template <typename Callback, typename Future> | |
static auto invoke(Callback&& cb, Future&& result) { | |
// Also need error propagation. Exercise for reader. | |
return std::apply(std::forward<Callback>(cb), std::move(result).get()); | |
} | |
}; | |
/////////////////////////////// | |
/// Specializations for void | |
// Uses default return traits. | |
template<> | |
struct future_invocation_traits<void> { | |
template <typename Callback, typename Future> | |
static auto invoke(Callback&& cb, Future&& result) { | |
// Also need error propagation. Exercise for reader. | |
return std::invoke(std::forward<Callback>(cb)); | |
} | |
}; | |
/////////////////////////////////////////// | |
/// Specializations for a Try/expected type | |
template <typename T> | |
struct expected { | |
std::optional<T> val; | |
std::exception_ptr err; | |
}; | |
template <typename T> | |
struct future_then_return_traits<expected<T>> { | |
using result_type = T; | |
template <typename Promise> | |
static void adapt(Promise&& p, expected<T>&& result) { | |
if (result.val) { | |
p.set_value(std::move(*result.val)); | |
} else { | |
p.set_error(std::move(result.err)); | |
} | |
} | |
}; | |
////////////////////////////////////////////////////////////////////////// | |
/// Specializations for a "keep" type that suppresses all special behavior | |
template <typename T> | |
struct keep { | |
T val; | |
}; | |
template <typename T> | |
struct future_then_return_traits<keep<T>> { | |
using result_type = T; | |
template <typename Promise> | |
static void adapt(Promise&& p, keep<T>&& result) { | |
p.set_value(std::move(result.val)); | |
} | |
}; | |
template <typename T> | |
struct future_invocation_traits<keep<T>> { | |
template <typename Callback, typename Future> | |
static auto invoke(Callback&& cb, Future&& result) { | |
// Also need error propagation. Exercise for reader. | |
return std::invoke(std::forward<Callback>(cb), std::move(result).get().val); | |
} | |
}; | |
////////////////////////////// | |
/// Example using these traits | |
template <typename T> | |
struct promise_t; | |
template <typename T> | |
struct future_t { | |
template < | |
// Callers can (but don't have to) explicitly specify the traits to use | |
// to customize how arguments will be passed to their callback. | |
template<typename> typename ArgTraits = future_invocation_traits, | |
typename Callback = void, // This needs to be deduced but can't be first. | |
typename RawResult = decltype(ArgTraits<T>::invoke(std::declval<Callback>(), | |
std::declval<future_t>())), | |
typename Result = typename future_then_return_traits<RawResult>::result_type | |
> | |
future_t<Result> then(Callback&& cb) { | |
promise_t<Result> p; | |
auto ret = p.get_future(); | |
onCompletion([cb = std::forward<Callback>(cb), p = std::move(p)] (future_t&& readyFut) { | |
future_then_return_traits<T>( | |
std::move(p), | |
ArgTraits<T>::invoke(cb, std::move(readyFut))); | |
}); | |
return ret; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment