Skip to content

Commit

Permalink
future: Add a futurize_base
Browse files Browse the repository at this point in the history
With 3 specializations it is easy to implement things a bit
differently in each. Fortunately, by moving a few definitions to a
futurize_base we can have a single futurize definition by using "if
constexpr" inside the functions.

IMHO this makes the code a lot easier to read. For example, we now have

template<typename T>
template<typename Func>
SEASTAR_CONCEPT( requires std::invocable<Func> )
void futurize<T>::satisfy_with_result_of(promise_base_with_type&& pr, Func&& func) {
    using ret_t = decltype(func());
    if constexpr (std::is_void_v<ret_t>) {
        func();
        pr.set_value();
    } else if constexpr (is_future<ret_t>::value) {
        func().forward_to(std::move(pr));
    } else {
        pr.set_value(func());
    }
}

Instead of 3 definitions.

Signed-off-by: Rafael Ávila de Espíndola <[email protected]>
Message-Id: <[email protected]>
  • Loading branch information
espindola authored and avikivity committed Aug 27, 2020
1 parent c9751d8 commit dd9e070
Showing 1 changed file with 80 additions and 201 deletions.
281 changes: 80 additions & 201 deletions include/seastar/core/future.hh
Original file line number Diff line number Diff line change
Expand Up @@ -1775,116 +1775,92 @@ private:
};


namespace internal {
template <typename T>
struct futurize {
struct futurize_base {
/// If \c T is a future, \c T; otherwise \c future<T>
using type = future<T>;
/// The promise type associated with \c type.
using promise_type = promise<T>;
/// The value tuple type associated with \c type
using value_type = typename type::value_type;

/// Apply a function to an argument list (expressed as a tuple)
/// and return the result, as a future (if it wasn't already).
template<typename Func, typename... FuncArgs>
static inline type apply(Func&& func, std::tuple<FuncArgs...>&& args) noexcept;

/// Invoke a function to an argument list
/// and return the result, as a future (if it wasn't already).
template<typename Func, typename... FuncArgs>
static inline type invoke(Func&& func, FuncArgs&&... args) noexcept;

/// Deprecated alias of invoke
template<typename Func, typename... FuncArgs>
[[deprecated("Use invoke for varargs")]]
static inline type apply(Func&& func, FuncArgs&&... args) noexcept;
using promise_base_with_type = internal::promise_base_with_type<T>;

/// Convert a value or a future to a future
static inline type convert(T&& value) { return make_ready_future<T>(std::move(value)); }
static inline type convert(type&& value) { return std::move(value); }

/// Convert the tuple representation into a future
static type from_tuple(value_type&& value);
/// Convert the tuple representation into a future
static type from_tuple(const value_type& value);

/// Makes an exceptional future of type \ref type.
template <typename Arg>
static type make_exception_future(Arg&& arg) noexcept;

private:
/// Forwards the result of, or exception thrown by, func() to the
/// promise. This avoids creating a future if func() doesn't
/// return one.
template<typename Func>
SEASTAR_CONCEPT( requires std::invocable<Func> )
static void satisfy_with_result_of(internal::promise_base_with_type<T>&&, Func&& func);

template <typename... U>
friend class future;
static inline type make_exception_future(Arg&& arg) noexcept;
};

/// \cond internal
template <>
struct futurize<void> {
struct futurize_base<void> {
using type = future<>;
using promise_type = promise<>;
using value_type = typename type::value_type;

template<typename Func, typename... FuncArgs>
static inline type apply(Func&& func, std::tuple<FuncArgs...>&& args) noexcept;

template<typename Func, typename... FuncArgs>
static inline type invoke(Func&& func, FuncArgs&&... args) noexcept;

template<typename Func, typename... FuncArgs>
[[deprecated("Use invoke for varargs")]]
static inline type apply(Func&& func, FuncArgs&&... args) noexcept;

static inline type from_tuple(value_type&& value);
static inline type from_tuple(const value_type& value);
using promise_base_with_type = internal::promise_base_with_type<>;

static inline type convert(type&& value) {
return std::move(value);
}
template <typename Arg>
static type make_exception_future(Arg&& arg) noexcept;
static inline type make_exception_future(Arg&& arg) noexcept;
};

private:
template<typename Func>
SEASTAR_CONCEPT( requires std::invocable<Func> )
static void satisfy_with_result_of(internal::promise_base_with_type<>&&, Func&& func);
template <typename T>
struct futurize_base<future<T>> : public futurize_base<T> {};

template <typename... U>
friend class future;
};
template <>
struct futurize_base<future<>> : public futurize_base<void> {};
}

template <typename... Args>
struct futurize<future<Args...>> {
using type = future<Args...>;
using promise_type = promise<Args...>;
template <typename T>
struct futurize : public internal::futurize_base<T> {
using base = internal::futurize_base<T>;
using type = typename base::type;
using promise_type = typename base::promise_type;
using promise_base_with_type = typename base::promise_base_with_type;
/// The value tuple type associated with \c type
using value_type = typename type::value_type;
using base::convert;
using base::make_exception_future;

/// Apply a function to an argument list (expressed as a tuple)
/// and return the result, as a future (if it wasn't already).
template<typename Func, typename... FuncArgs>
static inline type apply(Func&& func, std::tuple<FuncArgs...>&& args) noexcept;

/// Invoke a function to an argument list
/// and return the result, as a future (if it wasn't already).
template<typename Func, typename... FuncArgs>
static inline type invoke(Func&& func, FuncArgs&&... args) noexcept;

/// Deprecated alias of invoke
template<typename Func, typename... FuncArgs>
[[deprecated("Use invoke for varargs")]]
static inline type apply(Func&& func, FuncArgs&&... args) noexcept;

static inline type from_tuple(value_type&& value);
static inline type from_tuple(const value_type& value);
static inline type apply(Func&& func, FuncArgs&&... args) noexcept {
return invoke(std::forward<Func>(func), std::forward<FuncArgs>(args)...);
}

static inline type convert(Args&&... values) { return make_ready_future<Args...>(std::move(values)...); }
static inline type convert(type&& value) { return std::move(value); }
static type current_exception_as_future() noexcept {
return type(future_state_base::current_exception_future_marker());
}

template <typename Arg>
static type make_exception_future(Arg&& arg) noexcept;
/// Convert the tuple representation into a future
static type from_tuple(value_type&& value) {
return type(ready_future_marker(), std::move(value));
}
/// Convert the tuple representation into a future
static type from_tuple(const value_type& value) {
return type(ready_future_marker(), value);
}

private:
/// Forwards the result of, or exception thrown by, func() to the
/// promise. This avoids creating a future if func() doesn't
/// return one.
template<typename Func>
SEASTAR_CONCEPT( requires std::invocable<Func> )
static void satisfy_with_result_of(internal::promise_base_with_type<Args...>&&, Func&& func);
static void satisfy_with_result_of(promise_base_with_type&&, Func&& func);

template <typename... U>
friend class future;
Expand Down Expand Up @@ -1963,169 +1939,72 @@ template<typename T>
template<typename Func, typename... FuncArgs>
typename futurize<T>::type futurize<T>::apply(Func&& func, std::tuple<FuncArgs...>&& args) noexcept {
try {
return convert(std::apply(std::forward<Func>(func), std::move(args)));
using ret_t = decltype(std::apply(std::forward<Func>(func), std::move(args)));
if constexpr (std::is_void_v<ret_t>) {
std::apply(std::forward<Func>(func), std::move(args));
return make_ready_future<>();
} else if constexpr (is_future<ret_t>::value){
return std::apply(std::forward<Func>(func), std::move(args));
} else {
return convert(std::apply(std::forward<Func>(func), std::move(args)));
}
} catch (...) {
return current_exception_as_future<T>();
return current_exception_as_future();
}
}

template<typename T>
template<typename Func>
SEASTAR_CONCEPT( requires std::invocable<Func> )
void futurize<T>::satisfy_with_result_of(internal::promise_base_with_type<T>&& pr, Func&& func) {
pr.set_value(func());
}

template<typename T>
template<typename Func, typename... FuncArgs>
typename futurize<T>::type futurize<T>::invoke(Func&& func, FuncArgs&&... args) noexcept {
try {
return convert(func(std::forward<FuncArgs>(args)...));
} catch (...) {
return current_exception_as_future<T>();
void futurize<T>::satisfy_with_result_of(promise_base_with_type&& pr, Func&& func) {
using ret_t = decltype(func());
if constexpr (std::is_void_v<ret_t>) {
func();
pr.set_value();
} else if constexpr (is_future<ret_t>::value) {
func().forward_to(std::move(pr));
} else {
pr.set_value(func());
}
}

template<typename T>
template<typename Func, typename... FuncArgs>
typename futurize<T>::type futurize<T>::apply(Func&& func, FuncArgs&&... args) noexcept {
return invoke(std::forward<Func>(func), std::forward<FuncArgs>(args)...);
}

template<typename Func, typename... FuncArgs>
typename futurize<void>::type futurize<void>::apply(Func&& func, std::tuple<FuncArgs...>&& args) noexcept {
try {
std::apply(std::forward<Func>(func), std::move(args));
return make_ready_future<>();
} catch (...) {
return current_exception_as_future<>();
}
}

template<typename Func>
SEASTAR_CONCEPT( requires std::invocable<Func> )
void futurize<void>::satisfy_with_result_of(internal::promise_base_with_type<>&& pr, Func&& func) {
func();
pr.set_value();
}

template<typename Func, typename... FuncArgs>
typename futurize<void>::type futurize<void>::invoke(Func&& func, FuncArgs&&... args) noexcept {
try {
func(std::forward<FuncArgs>(args)...);
return make_ready_future<>();
} catch (...) {
return current_exception_as_future<>();
}
}

template<typename Func, typename... FuncArgs>
typename futurize<void>::type futurize<void>::apply(Func&& func, FuncArgs&&... args) noexcept {
return invoke(std::forward<Func>(func), std::forward<FuncArgs>(args)...);
}

template<typename... Args>
template<typename Func, typename... FuncArgs>
typename futurize<future<Args...>>::type futurize<future<Args...>>::apply(Func&& func, std::tuple<FuncArgs...>&& args) noexcept {
try {
return std::apply(std::forward<Func>(func), std::move(args));
} catch (...) {
return current_exception_as_future<Args...>();
}
}

template<typename... Args>
template<typename Func>
SEASTAR_CONCEPT( requires std::invocable<Func> )
void futurize<future<Args...>>::satisfy_with_result_of(internal::promise_base_with_type<Args...>&& pr, Func&& func) {
func().forward_to(std::move(pr));
}

template<typename... Args>
template<typename Func, typename... FuncArgs>
typename futurize<future<Args...>>::type futurize<future<Args...>>::invoke(Func&& func, FuncArgs&&... args) noexcept {
typename futurize<T>::type futurize<T>::invoke(Func&& func, FuncArgs&&... args) noexcept {
try {
return func(std::forward<FuncArgs>(args)...);
using ret_t = decltype(func(std::forward<FuncArgs>(args)...));
if constexpr (std::is_void_v<ret_t>) {
func(std::forward<FuncArgs>(args)...);
return make_ready_future<>();
} else if constexpr (is_future<ret_t>::value) {
return func(std::forward<FuncArgs>(args)...);
} else {
return convert(func(std::forward<FuncArgs>(args)...));
}
} catch (...) {
return current_exception_as_future<Args...>();
return current_exception_as_future();
}
}

template<typename... Args>
template<typename Func, typename... FuncArgs>
typename futurize<future<Args...>>::type futurize<future<Args...>>::apply(Func&& func, FuncArgs&&... args) noexcept {
return invoke(std::forward<Func>(func), std::forward<FuncArgs>(args)...);
}

template <typename T>
template <typename Arg>
inline
future<T>
futurize<T>::make_exception_future(Arg&& arg) noexcept {
internal::futurize_base<T>::make_exception_future(Arg&& arg) noexcept {
using ::seastar::make_exception_future;
using ::seastar::internal::make_exception_future;
return make_exception_future<T>(std::forward<Arg>(arg));
}

template <typename... T>
template <typename Arg>
inline
future<T...>
futurize<future<T...>>::make_exception_future(Arg&& arg) noexcept {
using ::seastar::make_exception_future;
using ::seastar::internal::make_exception_future;
return make_exception_future<T...>(std::forward<Arg>(arg));
}

template <typename Arg>
inline
future<>
futurize<void>::make_exception_future(Arg&& arg) noexcept {
internal::futurize_base<void>::make_exception_future(Arg&& arg) noexcept {
using ::seastar::make_exception_future;
using ::seastar::internal::make_exception_future;
return make_exception_future<>(std::forward<Arg>(arg));
}

template <typename T>
inline
future<T>
futurize<T>::from_tuple(value_type&& value) {
return make_ready_future<T>(std::move(value));
}

template <typename T>
inline
future<T>
futurize<T>::from_tuple(const value_type& value) {
return make_ready_future<T>(value);
}

inline
future<>
futurize<void>::from_tuple(value_type&& value) {
return make_ready_future<>();
}

inline
future<>
futurize<void>::from_tuple(const value_type& value) {
return make_ready_future<>();
}

template <typename... Args>
inline
future<Args...>
futurize<future<Args...>>::from_tuple(value_type&& value) {
return make_ready_future<Args...>(std::move(value));
}

template <typename... Args>
inline
future<Args...>
futurize<future<Args...>>::from_tuple(const value_type& value) {
return make_ready_future<Args...>(value);
}

template<typename Func, typename... Args>
auto futurize_invoke(Func&& func, Args&&... args) noexcept {
using futurator = futurize<std::result_of_t<Func(Args&&...)>>;
Expand Down

0 comments on commit dd9e070

Please sign in to comment.