summaryrefslogtreecommitdiff
path: root/src/mongo/util/future.h
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2019-05-15 16:12:21 -0400
committerMathias Stearn <mathias@10gen.com>2019-06-06 11:36:41 -0400
commite5115d5bf9761ba1bbaf058830bea713ef1cf9fc (patch)
tree7a18ca4c4b61fc70f26274b645f8247e13fdd01d /src/mongo/util/future.h
parent87f9f974744e15b8bf49823ff67cb6c5170a279b (diff)
downloadmongo-e5115d5bf9761ba1bbaf058830bea713ef1cf9fc.tar.gz
SERVER-41372 Constrain callback-taking methods on Futures
Diffstat (limited to 'src/mongo/util/future.h')
-rw-r--r--src/mongo/util/future.h112
1 files changed, 66 insertions, 46 deletions
diff --git a/src/mongo/util/future.h b/src/mongo/util/future.h
index 0dabd2c98ff..f46f3c541e1 100644
--- a/src/mongo/util/future.h
+++ b/src/mongo/util/future.h
@@ -144,7 +144,7 @@ public:
return SemiFuture(Impl::makeReady(std::move(val)));
}
- template <typename U = T, typename = std::enable_if_t<std::is_void_v<U>>>
+ REQUIRES_FOR_NON_TEMPLATE(std::is_void_v<T>)
static SemiFuture<void> makeReady() {
return SemiFuture(Impl::makeReady());
}
@@ -281,8 +281,8 @@ private:
};
// Deduction Guides
-template <typename T,
- typename = std::enable_if_t<!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>>>
+TEMPLATE(typename T)
+REQUIRES(!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>)
SemiFuture(T)->SemiFuture<T>;
template <typename T>
SemiFuture(StatusWith<T>)->SemiFuture<T>;
@@ -327,7 +327,7 @@ public:
static Future<T> makeReady(StatusWith<T_unless_void> val) {
return Future(Impl::makeReady(std::move(val)));
}
- template <typename U = T, typename = std::enable_if_t<std::is_void_v<U>>>
+ REQUIRES_FOR_NON_TEMPLATE(std::is_void_v<T>)
static Future<void> makeReady() {
return Future(Impl::makeReady());
}
@@ -343,8 +343,9 @@ public:
* For now, the callback must not fail, since there is nowhere to propagate the error to.
* TODO decide how to handle func throwing.
*/
- template <typename Func>
- void getAsync(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>)
+ void getAsync(Func&& func) && noexcept {
std::move(this->_impl).getAsync(std::forward<Func>(func));
}
@@ -383,8 +384,9 @@ public:
* The callback takes a T and can return anything (see above for how Statusy and Futurey returns
* are handled.)
*/
- template <typename Func>
- /*see above*/ auto then(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, T>)
+ /*see above*/ auto then(Func&& func) && noexcept {
return wrap<Func, T>(std::move(this->_impl).then(std::forward<Func>(func)));
}
@@ -395,8 +397,9 @@ public:
* The callback takes a StatusOrStatusWith<T> and can return anything (see above for how Statusy
* and Futurey returns are handled.)
*/
- template <typename Func>
- /*see above*/ auto onCompletion(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>)
+ /*see above*/ auto onCompletion(Func&& func) && noexcept {
return wrap<Func, Status>(std::move(this->_impl).onCompletion(std::forward<Func>(func)));
}
@@ -413,8 +416,9 @@ public:
* The callback takes a non-OK Status and returns a possibly-wrapped T (see above for how
* Statusy and Futurey returns are handled.)
*/
- template <typename Func>
- /*see above*/ auto onError(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ /*see above*/ auto onError(Func&& func) && noexcept {
return wrap<Func, Status>(std::move(this->_impl).onError(std::forward<Func>(func)));
}
@@ -425,8 +429,9 @@ public:
* The callback takes a non-OK Status and returns a possibly-wrapped T (see above for how
* Statusy and Futurey returns are handled.)
*/
- template <ErrorCodes::Error code, typename Func>
- /*see above*/ auto onError(Func&& func) && noexcept {
+ TEMPLATE(ErrorCodes::Error code, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ /*see above*/ auto onError(Func&& func) && noexcept {
return wrap<Func, Status>(
std::move(this->_impl).template onError<code>(std::forward<Func>(func)));
}
@@ -438,8 +443,9 @@ public:
* The callback takes a non-OK Status and returns a possibly-wrapped T (see above for how
* Statusy and Futurey returns are handled.)
*/
- template <ErrorCategory category, typename Func>
- /*see above*/ auto onErrorCategory(Func&& func) && noexcept {
+ TEMPLATE(ErrorCategory category, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ /*see above*/ auto onErrorCategory(Func&& func) && noexcept {
return wrap<Func, Status>(
std::move(this->_impl).template onErrorCategory<category>(std::forward<Func>(func)));
}
@@ -464,8 +470,9 @@ public:
*
* The callback takes a const T& and must return void.
*/
- template <typename Func>
- Future<T> tap(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const T>)
+ Future<T> tap(Func&& func) && noexcept {
return Future<T>(std::move(this->_impl).tap(std::forward<Func>(func)));
}
@@ -476,8 +483,9 @@ public:
*
* The callback takes a non-OK Status and must return void.
*/
- template <typename Func>
- Future<T> tapError(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const Status>)
+ Future<T> tapError(Func&& func) && noexcept {
return Future<T>(std::move(this->_impl).tapError(std::forward<Func>(func)));
}
@@ -489,8 +497,9 @@ public:
*
* The callback takes a StatusOrStatusWith<T> and must return void.
*/
- template <typename Func>
- Future<T> tapAll(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const StatusOrStatusWith<T>>)
+ Future<T> tapAll(Func&& func) && noexcept {
return Future<T>(std::move(this->_impl).tapAll(std::forward<Func>(func)));
}
@@ -514,8 +523,8 @@ private:
};
// Deduction Guides
-template <typename T,
- typename = std::enable_if_t<!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>>>
+TEMPLATE(typename T)
+REQUIRES(!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>)
Future(T)->Future<T>;
template <typename T>
Future(StatusWith<T>)->Future<T>;
@@ -556,7 +565,7 @@ public:
static_assert(!std::is_void_v<T>);
}
- template <typename U = T, typename = std::enable_if_t<std::is_void_v<U>>>
+ REQUIRES_FOR_NON_TEMPLATE(std::is_void_v<T>)
explicit ExecutorFuture(ExecutorPtr exec) : SemiFuture<void>(), _exec(std::move(exec)) {}
/**
@@ -592,8 +601,9 @@ public:
// it should be doable, but will be fairly complicated.
//
- template <typename Func>
- void getAsync(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>)
+ void getAsync(Func&& func) && noexcept {
static_assert(std::is_void_v<decltype(func(std::declval<StatusOrStatusWith<T>>()))>,
"func passed to getAsync must return void");
@@ -611,37 +621,42 @@ public:
});
}
- template <typename Func>
- auto then(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, T>)
+ auto then(Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec), std::move(this->_impl).then(wrapCB<T>(std::forward<Func>(func))));
}
- template <typename Func>
- auto onCompletion(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>)
+ auto onCompletion(Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
std::move(this->_impl)
.onCompletion(wrapCB<StatusOrStatusWith<T>>(std::forward<Func>(func))));
}
- template <typename Func>
- ExecutorFuture<T> onError(Func&& func) && noexcept {
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ ExecutorFuture<T> onError(Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
std::move(this->_impl).onError(wrapCB<Status>(std::forward<Func>(func))));
}
- template <ErrorCodes::Error code, typename Func>
- ExecutorFuture<T> onError(Func&& func) && noexcept {
+ TEMPLATE(ErrorCodes::Error code, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ ExecutorFuture<T> onError(Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
std::move(this->_impl)
.template onError<code>(wrapCB<Status>(std::forward<Func>(func))));
}
- template <ErrorCategory category, typename Func>
- ExecutorFuture<T> onErrorCategory(Func&& func) && noexcept {
+ TEMPLATE(ErrorCategory category, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ ExecutorFuture<T> onErrorCategory(Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
std::move(this->_impl)
@@ -692,8 +707,8 @@ private:
};
// Deduction Guides
-template <typename T,
- typename = std::enable_if_t<!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>>>
+TEMPLATE(typename T)
+REQUIRES(!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>)
ExecutorFuture(ExecutorPtr, T)->ExecutorFuture<T>;
template <typename T>
ExecutorFuture(ExecutorPtr, future_details::FutureImpl<T>)->ExecutorFuture<T>;
@@ -766,7 +781,8 @@ public:
* because this method will correctly propagate errors thrown from makeResult(), rather than
* ErrorCodes::BrokenPromise.
*/
- template <typename Func>
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, void>)
void setWith(Func&& func) noexcept {
setFrom(Future<void>::makeReady().then(std::forward<Func>(func)));
}
@@ -782,7 +798,8 @@ public:
});
}
- template <typename... Args>
+ TEMPLATE(typename... Args)
+ REQUIRES(std::is_constructible_v<T, Args...> || (std::is_void_v<T> && sizeof...(Args) == 0))
void emplaceValue(Args&&... args) noexcept {
setImpl([&](boost::intrusive_ptr<SharedStateT>&& sharedState) {
sharedState->emplaceValue(std::forward<Args>(args)...);
@@ -963,8 +980,8 @@ private:
};
// Deduction Guides
-template <typename T,
- typename = std::enable_if_t<!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>>>
+TEMPLATE(typename T)
+REQUIRES(!isStatusOrStatusWith<T> && !future_details::isFutureLike<T>)
SharedSemiFuture(T)->SharedSemiFuture<T>;
template <typename T>
SharedSemiFuture(StatusWith<T>)->SharedSemiFuture<T>;
@@ -1014,7 +1031,8 @@ public:
return SharedSemiFuture<T>(_sharedState);
}
- template <typename Func>
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, void>)
void setWith(Func&& func) noexcept {
invariant(!std::exchange(_haveCompleted, true));
setFrom(Future<void>::makeReady().then(std::forward<Func>(func)));
@@ -1025,7 +1043,8 @@ public:
std::move(future).propagateResultTo(_sharedState.get());
}
- template <typename... Args>
+ TEMPLATE(typename... Args)
+ REQUIRES(std::is_constructible_v<T, Args...> || (std::is_void_v<T> && sizeof...(Args) == 0))
void emplaceValue(Args&&... args) noexcept {
invariant(!std::exchange(_haveCompleted, true));
_sharedState->emplaceValue(std::forward<Args>(args)...);
@@ -1058,7 +1077,8 @@ private:
* Promise::setWith, and has the same reasons to prefer it over Future<T>::makeReady(). Also, it
* deduces the T, so it is easier to use.
*/
-template <typename Func>
+TEMPLATE(typename Func)
+REQUIRES(future_details::isCallable<Func, void>)
auto makeReadyFutureWith(Func&& func) {
return Future<void>::makeReady().then(std::forward<Func>(func));
}