mirror of
https://github.com/clearlinux/folly.git
synced 2026-09-01 02:34:43 +00:00
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
This commit is contained in:
+3
-1
@@ -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 \
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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
|
||||
+7
-341
@@ -32,174 +32,15 @@
|
||||
#include <folly/futures/FutureException.h>
|
||||
#include <folly/futures/detail/Types.h>
|
||||
|
||||
// boring predeclarations and details
|
||||
#include <folly/futures/Future-pre.h>
|
||||
|
||||
// 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>
|
||||
|
||||
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;
|
||||
|
||||
/// 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class Future {
|
||||
public:
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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
|
||||
Reference in New Issue
Block a user