зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1885840 - Add support for Maybe::andThen. r=glandium
Differential Revision: https://phabricator.services.mozilla.com/D204997
This commit is contained in:
Родитель
fdd885886a
Коммит
4ceb8a3f25
72
mfbt/Maybe.h
72
mfbt/Maybe.h
|
@ -9,6 +9,7 @@
|
|||
#ifndef mozilla_Maybe_h
|
||||
#define mozilla_Maybe_h
|
||||
|
||||
#include <functional>
|
||||
#include <new> // for placement new
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
@ -288,6 +289,15 @@ struct MaybeStorage<T, true> : MaybeStorageBase<T> {
|
|||
mIsSome{true} {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsMaybeImpl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsMaybeImpl<Maybe<T>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using IsMaybe = IsMaybeImpl<std::decay_t<T>>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename U = typename std::remove_cv<
|
||||
|
@ -665,6 +675,57 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
|
|||
return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{};
|
||||
}
|
||||
|
||||
/*
|
||||
* If |isSome()|, runs the provided function or functor on the contents of
|
||||
* this Maybe and returns the result. Note that the provided function or
|
||||
* functor must return a Maybe<U> of any type U.
|
||||
* If |isNothing()|, returns an empty Maybe value with the same type as what
|
||||
* the provided function would have returned.
|
||||
*/
|
||||
template <typename Func>
|
||||
constexpr auto andThen(Func&& aFunc) & {
|
||||
static_assert(std::is_invocable_v<Func, T&>);
|
||||
using U = std::invoke_result_t<Func, T&>;
|
||||
static_assert(detail::IsMaybe<U>::value);
|
||||
if (isSome()) {
|
||||
return std::invoke(std::forward<Func>(aFunc), ref());
|
||||
}
|
||||
return std::remove_cv_t<std::remove_reference_t<U>>{};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
constexpr auto andThen(Func&& aFunc) const& {
|
||||
static_assert(std::is_invocable_v<Func, const T&>);
|
||||
using U = std::invoke_result_t<Func, const T&>;
|
||||
static_assert(detail::IsMaybe<U>::value);
|
||||
if (isSome()) {
|
||||
return std::invoke(std::forward<Func>(aFunc), ref());
|
||||
}
|
||||
return std::remove_cv_t<std::remove_reference_t<U>>{};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
constexpr auto andThen(Func&& aFunc) && {
|
||||
static_assert(std::is_invocable_v<Func, T&&>);
|
||||
using U = std::invoke_result_t<Func, T&&>;
|
||||
static_assert(detail::IsMaybe<U>::value);
|
||||
if (isSome()) {
|
||||
return std::invoke(std::forward<Func>(aFunc), extract());
|
||||
}
|
||||
return std::remove_cv_t<std::remove_reference_t<U>>{};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
constexpr auto andThen(Func&& aFunc) const&& {
|
||||
static_assert(std::is_invocable_v<Func, const T&&>);
|
||||
using U = std::invoke_result_t<Func, const T&&>;
|
||||
static_assert(detail::IsMaybe<U>::value);
|
||||
if (isSome()) {
|
||||
return std::invoke(std::forward<Func>(aFunc), extract());
|
||||
}
|
||||
return std::remove_cv_t<std::remove_reference_t<U>>{};
|
||||
}
|
||||
|
||||
/* If |isSome()|, empties this Maybe and destroys its contents. */
|
||||
constexpr void reset() {
|
||||
if (isSome()) {
|
||||
|
@ -753,6 +814,17 @@ class Maybe<T&> {
|
|||
return val;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
constexpr auto andThen(Func&& aFunc) const {
|
||||
static_assert(std::is_invocable_v<Func, T&>);
|
||||
using U = std::invoke_result_t<Func, T&>;
|
||||
static_assert(detail::IsMaybe<U>::value);
|
||||
if (isSome()) {
|
||||
return std::invoke(std::forward<Func>(aFunc), ref());
|
||||
}
|
||||
return std::remove_cv_t<std::remove_reference_t<U>>{};
|
||||
}
|
||||
|
||||
bool refEquals(const Maybe<T&>& aOther) const {
|
||||
return mValue == aOther.mValue;
|
||||
}
|
||||
|
|
|
@ -1457,6 +1457,110 @@ static bool TestReference() {
|
|||
return true;
|
||||
}
|
||||
|
||||
static Maybe<int> IncrementAndReturnTag(BasicValue& aValue) {
|
||||
gFunctionWasApplied = true;
|
||||
aValue.SetTag(aValue.GetTag() + 1);
|
||||
return Some(aValue.GetTag());
|
||||
}
|
||||
|
||||
static Maybe<int> AccessValueAndReturnNothing(const BasicValue&) {
|
||||
gFunctionWasApplied = true;
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
static Maybe<int> AccessValueAndReturnOther(const BasicValue&) {
|
||||
gFunctionWasApplied = true;
|
||||
return Some(42);
|
||||
}
|
||||
|
||||
struct IncrementAndReturnTagFunctor {
|
||||
IncrementAndReturnTagFunctor() : mBy(1) {}
|
||||
|
||||
Maybe<int> operator()(BasicValue& aValue) {
|
||||
aValue.SetTag(aValue.GetTag() + mBy.GetTag());
|
||||
return Some(aValue.GetTag());
|
||||
}
|
||||
|
||||
BasicValue mBy;
|
||||
};
|
||||
|
||||
struct AccessValueAndReturnOtherFunctor {
|
||||
explicit AccessValueAndReturnOtherFunctor(int aVal) : mBy(aVal) {}
|
||||
|
||||
Maybe<BasicValue> operator()() {
|
||||
gFunctionWasApplied = true;
|
||||
return Some(mBy);
|
||||
}
|
||||
|
||||
BasicValue mBy;
|
||||
};
|
||||
|
||||
static bool TestAndThen() {
|
||||
// Check that andThen handles the 'Nothing' case.
|
||||
gFunctionWasApplied = false;
|
||||
Maybe<BasicValue> mayValue;
|
||||
Maybe<int> otherValue = mayValue.andThen(&AccessValueAndReturnOther);
|
||||
MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
|
||||
MOZ_RELEASE_ASSERT(otherValue.isNothing());
|
||||
|
||||
// Check that andThen handles the 'Some' case.
|
||||
mayValue = Some(BasicValue(1));
|
||||
otherValue = mayValue.andThen(&AccessValueAndReturnNothing);
|
||||
MOZ_RELEASE_ASSERT(gFunctionWasApplied);
|
||||
MOZ_RELEASE_ASSERT(otherValue.isNothing());
|
||||
gFunctionWasApplied = false;
|
||||
otherValue = mayValue.andThen(&IncrementAndReturnTag);
|
||||
MOZ_RELEASE_ASSERT(gFunctionWasApplied);
|
||||
MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 2);
|
||||
gFunctionWasApplied = false;
|
||||
otherValue = mayValue.andThen(&AccessValueAndReturnOther);
|
||||
MOZ_RELEASE_ASSERT(gFunctionWasApplied);
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 42);
|
||||
|
||||
// Check that andThen works with a const reference.
|
||||
const Maybe<BasicValue>& mayValueCRef = mayValue;
|
||||
gFunctionWasApplied = false;
|
||||
otherValue = mayValueCRef.andThen(&AccessValueAndReturnOther);
|
||||
MOZ_RELEASE_ASSERT(gFunctionWasApplied);
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 42);
|
||||
|
||||
// Check that andThen works with functors.
|
||||
IncrementAndReturnTagFunctor tagIncrementer;
|
||||
MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
|
||||
mayValue = Some(BasicValue(1));
|
||||
otherValue = mayValue.andThen(tagIncrementer);
|
||||
MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 2);
|
||||
MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
|
||||
|
||||
// Check that andThen works with lambda expressions.
|
||||
gFunctionWasApplied = false;
|
||||
mayValue = Some(BasicValue(2));
|
||||
otherValue = mayValue.andThen(
|
||||
[](BasicValue& aVal) { return Some(aVal.GetTag() * 2); });
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 4);
|
||||
otherValue = otherValue.andThen([](int aVal) { return Some(aVal * 2); });
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 8);
|
||||
otherValue = mayValueCRef.andThen([&](const BasicValue& aVal) {
|
||||
gFunctionWasApplied = true;
|
||||
return Some(42);
|
||||
});
|
||||
MOZ_RELEASE_ASSERT(gFunctionWasApplied == true);
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 42);
|
||||
|
||||
// Check that andThen can move the contained value.
|
||||
mayValue = Some(BasicValue(1));
|
||||
otherValue = std::move(mayValue).andThen([&](BasicValue&& aVal) {
|
||||
BasicValue tmp = std::move(aVal);
|
||||
return Some(tmp.GetTag());
|
||||
});
|
||||
MOZ_RELEASE_ASSERT(mayValue.isNothing());
|
||||
MOZ_RELEASE_ASSERT(*otherValue == 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// These are quasi-implementation details, but we assert them here to prevent
|
||||
// backsliding to earlier times when Maybe<T> for smaller T took up more space
|
||||
// than T's alignment required.
|
||||
|
@ -1486,6 +1590,7 @@ int main() {
|
|||
RUN_TEST(TestSomePointerConversion);
|
||||
RUN_TEST(TestTypeConversion);
|
||||
RUN_TEST(TestReference);
|
||||
RUN_TEST(TestAndThen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче