mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 1186693 - Add exhaustive matching to mozilla::Variant; r=Waldo
This commit is contained in:
parent
6de4674783
commit
26eaf483ae
@ -117,10 +117,15 @@ struct VariantImplementation<N, T> {
|
||||
|
||||
template<typename Variant>
|
||||
static bool
|
||||
equal(const Variant& aLhs, const Variant& aRhs)
|
||||
{
|
||||
equal(const Variant& aLhs, const Variant& aRhs) {
|
||||
return aLhs.template as<T>() == aRhs.template as<T>();
|
||||
}
|
||||
|
||||
template<typename Matcher, typename ConcreteVariant>
|
||||
static typename Matcher::ReturnType
|
||||
match(Matcher& aMatcher, ConcreteVariant& aV) {
|
||||
return aMatcher.match(aV.template as<T>());
|
||||
}
|
||||
};
|
||||
|
||||
// VariantImplementation for some variant type T.
|
||||
@ -171,6 +176,27 @@ struct VariantImplementation<N, T, Ts...>
|
||||
return Next::equal(aLhs, aRhs);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Matcher, typename ConcreteVariant>
|
||||
static typename Matcher::ReturnType
|
||||
match(Matcher& aMatcher, ConcreteVariant& aV)
|
||||
{
|
||||
if (aV.template is<T>()) {
|
||||
return aMatcher.match(aV.template as<T>());
|
||||
} else {
|
||||
// If you're seeing compilation errors here like "no matching
|
||||
// function for call to 'match'" then that means that the
|
||||
// Matcher doesn't exhaust all variant types. There must exist a
|
||||
// Matcher::match(T&) for every variant type T.
|
||||
//
|
||||
// If you're seeing compilation errors here like "cannot
|
||||
// initialize return object of type <...> with an rvalue of type
|
||||
// <...>" then that means that the Matcher::match(T&) overloads
|
||||
// are returning different types. They must all return the same
|
||||
// Matcher::ReturnType type.
|
||||
return Next::match(aMatcher, aV);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
@ -229,6 +255,36 @@ struct VariantImplementation<N, T, Ts...>
|
||||
* Variant<UniquePtr<A>, B, C> v(MakeUnique<A>());
|
||||
* auto ptr = v.extract<UniquePtr<A>>();
|
||||
*
|
||||
* Finally, you can exhaustively match on the contained variant and branch into
|
||||
* different code paths depending which type is contained. This is preferred to
|
||||
* manually checking every variant type T with is<T>() because it provides
|
||||
* compile-time checking that you handled every type, rather than runtime
|
||||
* assertion failures.
|
||||
*
|
||||
* // Bad!
|
||||
* char* foo(Variant<A, B, C, D>& v) {
|
||||
* if (v.is<A>()) {
|
||||
* return ...;
|
||||
* } else if (v.is<B>()) {
|
||||
* return ...;
|
||||
* } else {
|
||||
* return doSomething(v.as<C>()); // Forgot about case D!
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Good!
|
||||
* struct FooMatcher
|
||||
* {
|
||||
* using ReturnType = char*;
|
||||
* ReturnType match(A& a) { ... }
|
||||
* ReturnType match(B& b) { ... }
|
||||
* ReturnType match(C& c) { ... }
|
||||
* ReturnType match(D& d) { ... } // Compile-time error to forget D!
|
||||
* }
|
||||
* char* foo(Variant<A, B, C, D>& v) {
|
||||
* return v.match(FooMatcher());
|
||||
* }
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* A tree is either an empty leaf, or a node with a value and two children:
|
||||
@ -381,6 +437,22 @@ public:
|
||||
MOZ_ASSERT(is<T>());
|
||||
return T(Move(as<T>()));
|
||||
}
|
||||
|
||||
// Exhaustive matching of all variant types no the contained value.
|
||||
|
||||
/** Match on an immutable const reference. */
|
||||
template<typename Matcher>
|
||||
typename Matcher::ReturnType
|
||||
match(Matcher& aMatcher) const {
|
||||
return Impl::match(aMatcher, *this);
|
||||
}
|
||||
|
||||
/** Match on a mutable non-const reference. */
|
||||
template<typename Matcher>
|
||||
typename Matcher::ReturnType
|
||||
match(Matcher& aMatcher) {
|
||||
return Impl::match(aMatcher, *this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -123,6 +123,48 @@ testEquality()
|
||||
MOZ_RELEASE_ASSERT(v6 == v6);
|
||||
}
|
||||
|
||||
struct Describer
|
||||
{
|
||||
static const char* little;
|
||||
static const char* medium;
|
||||
static const char* big;
|
||||
|
||||
using ReturnType = const char*;
|
||||
|
||||
const char* match(const uint8_t&) { return little; }
|
||||
const char* match(const uint32_t&) { return medium; }
|
||||
const char* match(const uint64_t&) { return big; }
|
||||
};
|
||||
|
||||
const char* Describer::little = "little";
|
||||
const char* Describer::medium = "medium";
|
||||
const char* Describer::big = "big";
|
||||
|
||||
static void
|
||||
testMatching()
|
||||
{
|
||||
printf("testMatching\n");
|
||||
using V = Variant<uint8_t, uint32_t, uint64_t>;
|
||||
|
||||
Describer desc;
|
||||
|
||||
V v1(uint8_t(1));
|
||||
V v2(uint32_t(2));
|
||||
V v3(uint64_t(3));
|
||||
|
||||
MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::little);
|
||||
MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::medium);
|
||||
MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::big);
|
||||
|
||||
const V& constRef1 = v1;
|
||||
const V& constRef2 = v2;
|
||||
const V& constRef3 = v3;
|
||||
|
||||
MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::little);
|
||||
MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::medium);
|
||||
MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::big);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
@ -131,6 +173,7 @@ main()
|
||||
testMove();
|
||||
testDestructor();
|
||||
testEquality();
|
||||
testMatching();
|
||||
|
||||
printf("TestVariant OK!\n");
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user