Bug 1621865 - Variant matchers may optionally take the current index as first parameter - r=froydnj

Depends on D66719

Differential Revision: https://phabricator.services.mozilla.com/D66720

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Gerald Squelart 2020-03-13 21:56:47 +00:00
Родитель 9c1f5b9571
Коммит 056a867f22
2 изменённых файлов: 118 добавлений и 5 удалений

Просмотреть файл

@ -182,12 +182,22 @@ struct VariantImplementation<Tag, N, T> {
template <typename Matcher, typename ConcreteVariant>
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
if constexpr (std::is_invocable_v<Matcher, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
} else {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
}
}
template <typename ConcreteVariant, typename Matcher>
static decltype(auto) matchN(ConcreteVariant& aV, Matcher&& aMatcher) {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
if constexpr (std::is_invocable_v<Matcher, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
} else {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
}
}
};
@ -242,7 +252,12 @@ struct VariantImplementation<Tag, N, T, Ts...> {
template <typename Matcher, typename ConcreteVariant>
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
if (aV.template is<N>()) {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
if constexpr (std::is_invocable_v<Matcher, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
} else {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
}
} else {
// If you're seeing compilation errors here like "no matching
// function for call to 'match'" then that means that the
@ -260,7 +275,12 @@ struct VariantImplementation<Tag, N, T, Ts...> {
template <typename ConcreteVariant, typename Mi, typename... Ms>
static decltype(auto) matchN(ConcreteVariant& aV, Mi&& aMi, Ms&&... aMs) {
if (aV.template is<N>()) {
return std::forward<Mi>(aMi)(aV.template as<N>());
if constexpr (std::is_invocable_v<Mi, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Mi>(aMi)(Tag(N), aV.template as<N>());
} else {
return std::forward<Mi>(aMi)(aV.template as<N>());
}
} else {
// If you're seeing compilation errors here like "no matching
// function for call to 'match'" then that means that the
@ -462,7 +482,7 @@ struct VariantIndex {
*
* // In some situations, a single generic lambda may also be appropriate:
* char* foo(Variant<A, B, C, D>& v) {
* return v.match([](auto&){...});
* return v.match([](auto&) {...});
* }
*
* // Alternatively, multiple function objects may be provided, each one
@ -474,6 +494,16 @@ struct VariantIndex {
* [](D&) { ... });
* }
*
* // In rare cases, the index of the currently-active alternative is
* // needed, it may be obtained by adding a first parameter in the matcner
* // callback, which will receive the index in its most compact type (just
* // use `size_t` if the exact type is not important), e.g.:
* char* foo(Variant<A, B, C, D>& v) {
* return v.match([](auto aIndex, auto& aAlternative) {...});
* // --OR--
* return v.match([](size_t aIndex, auto& aAlternative) {...});
* }
*
* ## Examples
*
* A tree is either an empty leaf, or a node with a value and two children:

Просмотреть файл

@ -550,6 +550,50 @@ static void testMatchingLambda() {
MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::bigConst);
}
static void testMatchingLambdaWithIndex() {
printf("testMatchingLambdaWithIndex\n");
using V = Variant<uint8_t, uint32_t, uint64_t>;
// Note: Lambdas' call operators are const by default (unless the lambda is
// declared `mutable`), hence the use of "...Const" strings below.
// There is no need to test mutable lambdas, nor rvalue lambda, because there
// would be no way to distinguish how each lambda is actually invoked because
// there is only one choice of call operator in each overload set.
auto desc = [](auto aIndex, auto& a) {
static_assert(sizeof(aIndex) < sizeof(size_t), "Expected small index type");
switch (sizeof(a)) {
case 1:
MOZ_RELEASE_ASSERT(aIndex == 0);
return Describer::littleConst;
case 4:
MOZ_RELEASE_ASSERT(aIndex == 1);
return Describer::mediumConst;
case 8:
MOZ_RELEASE_ASSERT(aIndex == 2);
return Describer::bigConst;
default:
MOZ_RELEASE_ASSERT(false);
return "";
}
};
V v1(uint8_t(1));
V v2(uint32_t(2));
V v3(uint64_t(3));
MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::littleConst);
MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::bigConst);
const V& constRef1 = v1;
const V& constRef2 = v2;
const V& constRef3 = v3;
MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::littleConst);
MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::bigConst);
}
static void testMatchingLambdas() {
printf("testMatchingLambdas\n");
using V = Variant<uint8_t, uint32_t, uint64_t>;
@ -578,6 +622,43 @@ static void testMatchingLambdas() {
Describer::bigConst);
}
static void testMatchingLambdasWithIndex() {
printf("testMatchingLambdasWithIndex\n");
using V = Variant<uint8_t, uint32_t, uint64_t>;
auto desc8 = [](size_t aIndex, const uint8_t& a) {
MOZ_RELEASE_ASSERT(aIndex == 0);
return Describer::littleConst;
};
auto desc32 = [](size_t aIndex, const uint32_t& a) {
MOZ_RELEASE_ASSERT(aIndex == 1);
return Describer::mediumConst;
};
auto desc64 = [](size_t aIndex, const uint64_t& a) {
MOZ_RELEASE_ASSERT(aIndex == 2);
return Describer::bigConst;
};
V v1(uint8_t(1));
V v2(uint32_t(2));
V v3(uint64_t(3));
MOZ_RELEASE_ASSERT(v1.match(desc8, desc32, desc64) == Describer::littleConst);
MOZ_RELEASE_ASSERT(v2.match(desc8, desc32, desc64) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(v3.match(desc8, desc32, desc64) == Describer::bigConst);
const V& constRef1 = v1;
const V& constRef2 = v2;
const V& constRef3 = v3;
MOZ_RELEASE_ASSERT(constRef1.match(desc8, desc32, desc64) ==
Describer::littleConst);
MOZ_RELEASE_ASSERT(constRef2.match(desc8, desc32, desc64) ==
Describer::mediumConst);
MOZ_RELEASE_ASSERT(constRef3.match(desc8, desc32, desc64) ==
Describer::bigConst);
}
static void testAddTagToHash() {
printf("testAddToHash\n");
using V = Variant<uint8_t, uint16_t, uint32_t, uint64_t>;
@ -623,7 +704,9 @@ int main() {
testEquality();
testMatching();
testMatchingLambda();
testMatchingLambdaWithIndex();
testMatchingLambdas();
testMatchingLambdasWithIndex();
testAddTagToHash();
printf("TestVariant OK!\n");