Commit abdd128c authored by Hans Fugal's avatar Hans Fugal Committed by Alecs King

rearrange Future.h

Summary: Move boring preamble stuff to `Future-pre.h` and `folly::futures` and `makeFuture` and pals to `helpers.h`.

Test Plan:
tests still build and pass

Reviewed By: jsedgwick@fb.com

Subscribers: exa, folly-diffs@, jsedgwick, yfeldblum, chalfant, hannesr, davejwatson

FB internal diff: D2014330

Signature: t1:2014330:1429941589:1e2c336136f3375f9b96e5df8c06ca5820ba6aeb
parent 3444ffba
......@@ -122,8 +122,10 @@ nobase_follyinclude_HEADERS = \
Format-inl.h \
futures/Deprecated.h \
futures/DrivableExecutor.h \
futures/Future-inl.h \
futures/Future-pre.h \
futures/helpers.h \
futures/Future.h \
futures/Future-inl.h \
futures/FutureException.h \
futures/InlineExecutor.h \
futures/ManualExecutor.h \
......
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
// included by Future.h, do not include directly.
namespace folly {
template <class> struct Promise;
template <typename T>
struct isFuture : std::false_type {
typedef T Inner;
};
template <typename T>
struct isFuture<Future<T>> : std::true_type {
typedef T Inner;
};
template <typename T>
struct isTry : std::false_type {};
template <typename T>
struct isTry<Try<T>> : std::true_type {};
namespace detail {
template <class> struct Core;
template <class...> struct VariadicContext;
template <class> struct CollectContext;
template<typename F, typename... Args>
using resultOf = decltype(std::declval<F>()(std::declval<Args>()...));
template <typename...>
struct ArgType;
template <typename Arg, typename... Args>
struct ArgType<Arg, Args...> {
typedef Arg FirstArg;
};
template <>
struct ArgType<> {
typedef void FirstArg;
};
template <bool isTry, typename F, typename... Args>
struct argResult {
typedef resultOf<F, Args...> Result;
};
template<typename F, typename... Args>
struct callableWith {
template<typename T,
typename = detail::resultOf<T, Args...>>
static constexpr std::true_type
check(std::nullptr_t) { return std::true_type{}; };
template<typename>
static constexpr std::false_type
check(...) { return std::false_type{}; };
typedef decltype(check<F>(nullptr)) type;
static constexpr bool value = type::value;
};
template<typename T, typename F>
struct callableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, T&&>::value,
detail::argResult<false, F, T&&>,
typename std::conditional<
callableWith<F, T&>::value,
detail::argResult<false, F, T&>,
typename std::conditional<
callableWith<F, Try<T>&&>::value,
detail::argResult<true, F, Try<T>&&>,
detail::argResult<true, F, Try<T>&>>::type>::type>::type>::type Arg;
typedef isFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template<typename F>
struct callableResult<void, F> {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, Try<void>&&>::value,
detail::argResult<true, F, Try<void>&&>,
detail::argResult<true, F, Try<void>&>>::type>::type Arg;
typedef isFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename L>
struct Extract : Extract<decltype(&L::operator())> { };
template <typename Class, typename R, typename... Args>
struct Extract<R(Class::*)(Args...) const> {
typedef isFuture<R> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner RawReturn;
typedef typename ArgType<Args...>::FirstArg FirstArg;
};
template <typename Class, typename R, typename... Args>
struct Extract<R(Class::*)(Args...)> {
typedef isFuture<R> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner RawReturn;
typedef typename ArgType<Args...>::FirstArg FirstArg;
};
} // detail
struct Timekeeper;
} // namespace
......@@ -32,173 +32,14 @@
#include <folly/futures/FutureException.h>
#include <folly/futures/detail/Types.h>
namespace folly {
template <class> struct Promise;
template <typename T>
struct isFuture : std::false_type {
typedef T Inner;
};
template <typename T>
struct isFuture<Future<T>> : std::true_type {
typedef T Inner;
};
template <typename T>
struct isTry : std::false_type {};
template <typename T>
struct isTry<Try<T>> : std::true_type {};
namespace detail {
template <class> struct Core;
template <class...> struct VariadicContext;
template <class> struct CollectContext;
template<typename F, typename... Args>
using resultOf = decltype(std::declval<F>()(std::declval<Args>()...));
template <typename...>
struct ArgType;
// boring predeclarations and details
#include <folly/futures/Future-pre.h>
template <typename Arg, typename... Args>
struct ArgType<Arg, Args...> {
typedef Arg FirstArg;
};
template <>
struct ArgType<> {
typedef void FirstArg;
};
template <bool isTry, typename F, typename... Args>
struct argResult {
typedef resultOf<F, Args...> Result;
};
template<typename F, typename... Args>
struct callableWith {
template<typename T,
typename = detail::resultOf<T, Args...>>
static constexpr std::true_type
check(std::nullptr_t) { return std::true_type{}; };
template<typename>
static constexpr std::false_type
check(...) { return std::false_type{}; };
typedef decltype(check<F>(nullptr)) type;
static constexpr bool value = type::value;
};
template<typename T, typename F>
struct callableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, T&&>::value,
detail::argResult<false, F, T&&>,
typename std::conditional<
callableWith<F, T&>::value,
detail::argResult<false, F, T&>,
typename std::conditional<
callableWith<F, Try<T>&&>::value,
detail::argResult<true, F, Try<T>&&>,
detail::argResult<true, F, Try<T>&>>::type>::type>::type>::type Arg;
typedef isFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
// not-boring helpers, e.g. all in folly::futures, makeFuture variants, etc.
// Needs to be included after Future-pre.h and before Future-inl.h
#include <folly/futures/helpers.h>
template<typename F>
struct callableResult<void, F> {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, Try<void>&&>::value,
detail::argResult<true, F, Try<void>&&>,
detail::argResult<true, F, Try<void>&>>::type>::type Arg;
typedef isFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename L>
struct Extract : Extract<decltype(&L::operator())> { };
template <typename Class, typename R, typename... Args>
struct Extract<R(Class::*)(Args...) const> {
typedef isFuture<R> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner RawReturn;
typedef typename ArgType<Args...>::FirstArg FirstArg;
};
template <typename Class, typename R, typename... Args>
struct Extract<R(Class::*)(Args...)> {
typedef isFuture<R> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
typedef typename ReturnsFuture::Inner RawReturn;
typedef typename ArgType<Args...>::FirstArg FirstArg;
};
} // detail
struct Timekeeper;
/// This namespace is for utility functions that would usually be static
/// members of Future, except they don't make sense there because they don't
/// depend on the template type (rather, on the type of their arguments in
/// some cases). This is the least-bad naming scheme we could think of. Some
/// of the functions herein have really-likely-to-collide names, like "map"
/// and "sleep".
namespace futures {
/// Returns a Future that will complete after the specified duration. The
/// Duration typedef of a `std::chrono` duration type indicates the
/// resolution you can expect to be meaningful (milliseconds at the time of
/// writing). Normally you wouldn't need to specify a Timekeeper, we will
/// use the global futures timekeeper (we run a thread whose job it is to
/// keep time for futures timeouts) but we provide the option for power
/// users.
///
/// The Timekeeper thread will be lazily created the first time it is
/// needed. If your program never uses any timeouts or other time-based
/// Futures you will pay no Timekeeper thread overhead.
Future<void> sleep(Duration, Timekeeper* = nullptr);
/// Create a Future chain from a sequence of callbacks. i.e.
///
/// f.then(a).then(b).then(c);
///
/// where f is a Future<A> and the result of the chain is a Future<Z>
/// becomes
///
/// f.then(chain<A,Z>(a, b, c));
// If anyone figures how to get chain to deduce A and Z, I'll buy you a drink.
template <class A, class Z, class... Callbacks>
std::function<Future<Z>(Try<A>)>
chain(Callbacks... fns);
/**
* Set func as the callback for each input Future and return a vector of
* Futures containing the results in the input order.
*/
template <class It, class F,
class ItT = typename std::iterator_traits<It>::value_type,
class Result = decltype(std::declval<ItT>().then(std::declval<F>()))>
std::vector<Future<Result>> map(It first, It last, F func);
// Sugar for the most common case
template <class Collection, class F>
auto map(Collection&& c, F&& func)
-> decltype(map(c.begin(), c.end(), func)) {
return map(c.begin(), c.end(), std::forward<F>(func));
}
}
namespace folly {
template <class T>
class Future {
......@@ -562,181 +403,6 @@ class Future {
void setExecutor(Executor* x) { core_->setExecutor(x); }
};
/**
Make a completed Future by moving in a value. e.g.
string foo = "foo";
auto f = makeFuture(std::move(foo));
or
auto f = makeFuture<string>("foo");
*/
template <class T>
Future<typename std::decay<T>::type> makeFuture(T&& t);
/** Make a completed void Future. */
Future<void> makeFuture();
/** Make a completed Future by executing a function. If the function throws
we capture the exception, otherwise we capture the result. */
template <class F>
auto makeFutureWith(
F&& func,
typename std::enable_if<
!std::is_reference<F>::value, bool>::type sdf = false)
-> Future<decltype(func())>;
template <class F>
auto makeFutureWith(
F const& func)
-> Future<decltype(func())>;
/// Make a failed Future from an exception_ptr.
/// Because the Future's type cannot be inferred you have to specify it, e.g.
///
/// auto f = makeFuture<string>(std::current_exception());
template <class T>
Future<T> makeFuture(std::exception_ptr const& e) DEPRECATED;
/// Make a failed Future from an exception_wrapper.
template <class T>
Future<T> makeFuture(exception_wrapper ew);
/** Make a Future from an exception type E that can be passed to
std::make_exception_ptr(). */
template <class T, class E>
typename std::enable_if<std::is_base_of<std::exception, E>::value,
Future<T>>::type
makeFuture(E const& e);
/** Make a Future out of a Try */
template <class T>
Future<T> makeFuture(Try<T>&& t);
/*
* Return a new Future that will call back on the given Executor.
* This is just syntactic sugar for makeFuture().via(executor)
*
* @param executor the Executor to call back on
*
* @returns a void Future that will call back on the given executor
*/
template <typename Executor>
Future<void> via(Executor* executor);
/** 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.
This function is thread-safe for Futures running on different threads. But
if you are doing anything non-trivial after, you will probably want to
follow with `via(executor)` because it will complete in whichever thread the
last Future completes in.
The return type for Future<T> input is a Future<std::vector<Try<T>>>
*/
template <class InputIterator>
Future<std::vector<Try<
typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAll(InputIterator first, InputIterator last);
// Sugar for the most common case
template <class Collection>
auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {
return collectAll(c.begin(), c.end());
}
/// This version takes a varying number of Futures instead of an iterator.
/// The return type for (Future<T1>, Future<T2>, ...) input
/// is a Future<std::tuple<Try<T1>, Try<T2>, ...>>.
/// The Futures are moved in, so your copies are invalid.
template <typename... Fs>
typename detail::VariadicContext<
typename std::decay<Fs>::type::value_type...>::type
collectAll(Fs&&... fs);
/// 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 <class InputIterator>
Future<typename detail::CollectContext<
typename std::iterator_traits<InputIterator>::value_type::value_type
>::result_type>
collect(InputIterator first, InputIterator last);
// Sugar for the most common case
template <class Collection>
auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) {
return collect(c.begin(), c.end());
}
/** The result is a pair of the index of the first Future to complete and
the Try. If multiple Futures complete at the same time (or are already
complete when passed in), the "winner" is chosen non-deterministically.
This function is thread-safe for Futures running on different threads.
*/
template <class InputIterator>
Future<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAny(InputIterator first, InputIterator last);
// Sugar for the most common case
template <class Collection>
auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) {
return collectAny(c.begin(), c.end());
}
/** when n Futures have completed, the Future completes with a vector of
the index and Try of those n Futures (the indices refer to the original
order, but the result vector will be in an arbitrary order)
Not thread safe.
*/
template <class InputIterator>
Future<std::vector<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
collectN(InputIterator first, InputIterator last, size_t n);
// Sugar for the most common case
template <class Collection>
auto collectN(Collection&& c, size_t n)
-> decltype(collectN(c.begin(), c.end(), n)) {
return collectN(c.begin(), c.end(), n);
}
template <typename F, typename T, typename ItT>
using MaybeTryArg = typename std::conditional<
detail::callableWith<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type;
template<typename F, typename T, typename Arg>
using isFutureResult = isFuture<typename std::result_of<F(T&&, Arg&&)>::type>;
/** repeatedly calls func on every result, e.g.
reduce(reduce(reduce(T initial, result of first), result of second), ...)
The type of the final result is a Future of the type of the initial value.
Func can either return a T, or a Future<T>
*/
template <class It, class T, class F,
class ItT = typename std::iterator_traits<It>::value_type::value_type,
class Arg = MaybeTryArg<F, T, ItT>>
typename std::enable_if<!isFutureResult<F, T, Arg>::value, Future<T>>::type
reduce(It first, It last, T initial, F func);
template <class It, class T, class F,
class ItT = typename std::iterator_traits<It>::value_type::value_type,
class Arg = MaybeTryArg<F, T, ItT>>
typename std::enable_if<isFutureResult<F, T, Arg>::value, Future<T>>::type
reduce(It first, It last, T initial, F func);
// Sugar for the most common case
template <class Collection, class T, class F>
......
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/futures/Future.h>
namespace folly {
/// This namespace is for utility functions that would usually be static
/// members of Future, except they don't make sense there because they don't
/// depend on the template type (rather, on the type of their arguments in
/// some cases). This is the least-bad naming scheme we could think of. Some
/// of the functions herein have really-likely-to-collide names, like "map"
/// and "sleep".
namespace futures {
/// Returns a Future that will complete after the specified duration. The
/// Duration typedef of a `std::chrono` duration type indicates the
/// resolution you can expect to be meaningful (milliseconds at the time of
/// writing). Normally you wouldn't need to specify a Timekeeper, we will
/// use the global futures timekeeper (we run a thread whose job it is to
/// keep time for futures timeouts) but we provide the option for power
/// users.
///
/// The Timekeeper thread will be lazily created the first time it is
/// needed. If your program never uses any timeouts or other time-based
/// Futures you will pay no Timekeeper thread overhead.
Future<void> sleep(Duration, Timekeeper* = nullptr);
/// Create a Future chain from a sequence of callbacks. i.e.
///
/// f.then(a).then(b).then(c);
///
/// where f is a Future<A> and the result of the chain is a Future<Z>
/// becomes
///
/// f.then(chain<A,Z>(a, b, c));
// If anyone figures how to get chain to deduce A and Z, I'll buy you a drink.
template <class A, class Z, class... Callbacks>
std::function<Future<Z>(Try<A>)>
chain(Callbacks... fns);
/**
* Set func as the callback for each input Future and return a vector of
* Futures containing the results in the input order.
*/
template <class It, class F,
class ItT = typename std::iterator_traits<It>::value_type,
class Result = decltype(std::declval<ItT>().then(std::declval<F>()))>
std::vector<Future<Result>> map(It first, It last, F func);
// Sugar for the most common case
template <class Collection, class F>
auto map(Collection&& c, F&& func)
-> decltype(map(c.begin(), c.end(), func)) {
return map(c.begin(), c.end(), std::forward<F>(func));
}
}
/**
Make a completed Future by moving in a value. e.g.
string foo = "foo";
auto f = makeFuture(std::move(foo));
or
auto f = makeFuture<string>("foo");
*/
template <class T>
Future<typename std::decay<T>::type> makeFuture(T&& t);
/** Make a completed void Future. */
Future<void> makeFuture();
/** Make a completed Future by executing a function. If the function throws
we capture the exception, otherwise we capture the result. */
template <class F>
auto makeFutureWith(
F&& func,
typename std::enable_if<
!std::is_reference<F>::value, bool>::type sdf = false)
-> Future<decltype(func())>;
template <class F>
auto makeFutureWith(
F const& func)
-> Future<decltype(func())>;
/// Make a failed Future from an exception_ptr.
/// Because the Future's type cannot be inferred you have to specify it, e.g.
///
/// auto f = makeFuture<string>(std::current_exception());
template <class T>
Future<T> makeFuture(std::exception_ptr const& e) DEPRECATED;
/// Make a failed Future from an exception_wrapper.
template <class T>
Future<T> makeFuture(exception_wrapper ew);
/** Make a Future from an exception type E that can be passed to
std::make_exception_ptr(). */
template <class T, class E>
typename std::enable_if<std::is_base_of<std::exception, E>::value,
Future<T>>::type
makeFuture(E const& e);
/** Make a Future out of a Try */
template <class T>
Future<T> makeFuture(Try<T>&& t);
/*
* Return a new Future that will call back on the given Executor.
* This is just syntactic sugar for makeFuture().via(executor)
*
* @param executor the Executor to call back on
*
* @returns a void Future that will call back on the given executor
*/
template <typename Executor>
Future<void> via(Executor* executor);
/** 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.
This function is thread-safe for Futures running on different threads. But
if you are doing anything non-trivial after, you will probably want to
follow with `via(executor)` because it will complete in whichever thread the
last Future completes in.
The return type for Future<T> input is a Future<std::vector<Try<T>>>
*/
template <class InputIterator>
Future<std::vector<Try<
typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAll(InputIterator first, InputIterator last);
// Sugar for the most common case
template <class Collection>
auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {
return collectAll(c.begin(), c.end());
}
/// This version takes a varying number of Futures instead of an iterator.
/// The return type for (Future<T1>, Future<T2>, ...) input
/// is a Future<std::tuple<Try<T1>, Try<T2>, ...>>.
/// The Futures are moved in, so your copies are invalid.
template <typename... Fs>
typename detail::VariadicContext<
typename std::decay<Fs>::type::value_type...>::type
collectAll(Fs&&... fs);
/// 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 <class InputIterator>
Future<typename detail::CollectContext<
typename std::iterator_traits<InputIterator>::value_type::value_type
>::result_type>
collect(InputIterator first, InputIterator last);
// Sugar for the most common case
template <class Collection>
auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) {
return collect(c.begin(), c.end());
}
/** The result is a pair of the index of the first Future to complete and
the Try. If multiple Futures complete at the same time (or are already
complete when passed in), the "winner" is chosen non-deterministically.
This function is thread-safe for Futures running on different threads.
*/
template <class InputIterator>
Future<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAny(InputIterator first, InputIterator last);
// Sugar for the most common case
template <class Collection>
auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) {
return collectAny(c.begin(), c.end());
}
/** when n Futures have completed, the Future completes with a vector of
the index and Try of those n Futures (the indices refer to the original
order, but the result vector will be in an arbitrary order)
Not thread safe.
*/
template <class InputIterator>
Future<std::vector<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
collectN(InputIterator first, InputIterator last, size_t n);
// Sugar for the most common case
template <class Collection>
auto collectN(Collection&& c, size_t n)
-> decltype(collectN(c.begin(), c.end(), n)) {
return collectN(c.begin(), c.end(), n);
}
template <typename F, typename T, typename ItT>
using MaybeTryArg = typename std::conditional<
detail::callableWith<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type;
template<typename F, typename T, typename Arg>
using isFutureResult = isFuture<typename std::result_of<F(T&&, Arg&&)>::type>;
/** repeatedly calls func on every result, e.g.
reduce(reduce(reduce(T initial, result of first), result of second), ...)
The type of the final result is a Future of the type of the initial value.
Func can either return a T, or a Future<T>
*/
template <class It, class T, class F,
class ItT = typename std::iterator_traits<It>::value_type::value_type,
class Arg = MaybeTryArg<F, T, ItT>>
typename std::enable_if<!isFutureResult<F, T, Arg>::value, Future<T>>::type
reduce(It first, It last, T initial, F func);
template <class It, class T, class F,
class ItT = typename std::iterator_traits<It>::value_type::value_type,
class Arg = MaybeTryArg<F, T, ItT>>
typename std::enable_if<isFutureResult<F, T, Arg>::value, Future<T>>::type
reduce(It first, It last, T initial, F func);
} // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment