diff --git a/mfbt/Variant.h b/mfbt/Variant.h index 71ed4722c908..947a57a6bbba 100644 --- a/mfbt/Variant.h +++ b/mfbt/Variant.h @@ -25,6 +25,22 @@ class Variant; namespace detail { +// Nth::Type is the Nth type (0-based) in the list of types Ts. +template +struct Nth; + +template +struct Nth<0, T, Ts...> +{ + using Type = T; +}; + +template +struct Nth +{ + using Type = typename Nth::Type; +}; + template struct FirstTypeIsInRest; @@ -180,31 +196,31 @@ struct VariantImplementation template static void copyConstruct(void* aLhs, const Variant& aRhs) { - ::new (KnownNotNull, aLhs) T(aRhs.template as()); + ::new (KnownNotNull, aLhs) T(aRhs.template as()); } template static void moveConstruct(void* aLhs, Variant&& aRhs) { - ::new (KnownNotNull, aLhs) T(aRhs.template extract()); + ::new (KnownNotNull, aLhs) T(aRhs.template extract()); } template static void destroy(Variant& aV) { - aV.template as().~T(); + aV.template as().~T(); } template static bool equal(const Variant& aLhs, const Variant& aRhs) { - return aLhs.template as() == aRhs.template as(); + return aLhs.template as() == aRhs.template as(); } template static auto match(Matcher&& aMatcher, ConcreteVariant& aV) - -> decltype(aMatcher.match(aV.template as())) + -> decltype(aMatcher.match(aV.template as())) { - return aMatcher.match(aV.template as()); + return aMatcher.match(aV.template as()); } }; @@ -222,8 +238,8 @@ struct VariantImplementation template static void copyConstruct(void* aLhs, const Variant& aRhs) { - if (aRhs.template is()) { - ::new (KnownNotNull, aLhs) T(aRhs.template as()); + if (aRhs.template is()) { + ::new (KnownNotNull, aLhs) T(aRhs.template as()); } else { Next::copyConstruct(aLhs, aRhs); } @@ -231,8 +247,8 @@ struct VariantImplementation template static void moveConstruct(void* aLhs, Variant&& aRhs) { - if (aRhs.template is()) { - ::new (KnownNotNull, aLhs) T(aRhs.template extract()); + if (aRhs.template is()) { + ::new (KnownNotNull, aLhs) T(aRhs.template extract()); } else { Next::moveConstruct(aLhs, Move(aRhs)); } @@ -240,8 +256,8 @@ struct VariantImplementation template static void destroy(Variant& aV) { - if (aV.template is()) { - aV.template as().~T(); + if (aV.template is()) { + aV.template as().~T(); } else { Next::destroy(aV); } @@ -249,9 +265,9 @@ struct VariantImplementation template static bool equal(const Variant& aLhs, const Variant& aRhs) { - if (aLhs.template is()) { - MOZ_ASSERT(aRhs.template is()); - return aLhs.template as() == aRhs.template as(); + if (aLhs.template is()) { + MOZ_ASSERT(aRhs.template is()); + return aLhs.template as() == aRhs.template as(); } else { return Next::equal(aLhs, aRhs); } @@ -260,10 +276,10 @@ struct VariantImplementation template static auto match(Matcher&& aMatcher, ConcreteVariant& aV) - -> decltype(aMatcher.match(aV.template as())) + -> decltype(aMatcher.match(aV.template as())) { - if (aV.template is()) { - return aMatcher.match(aV.template as()); + if (aV.template is()) { + return aMatcher.match(aV.template as()); } else { // If you're seeing compilation errors here like "no matching // function for call to 'match'" then that means that the @@ -349,6 +365,7 @@ struct AsVariantTemporary * Variant v1 = Foo(); // v1 holds char('x'). * * All access to the contained value goes through type-safe accessors. + * Either the stored type, or the type index may be provided. * * void * Foo(Variant v) @@ -356,6 +373,8 @@ struct AsVariantTemporary * if (v.is()) { * A& ref = v.as(); * ... + * } else (v.is<1>()) { // Same as v.is in this case. + * ... * } else { * ... * } @@ -382,8 +401,8 @@ struct AsVariantTemporary * auto ptr = v.extract>(); * * 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() because it provides + * different code paths depending on which type is contained. This is preferred + * to manually checking every variant type T with is() because it provides * compile-time checking that you handled every type, rather than runtime * assertion failures. * @@ -533,7 +552,7 @@ public: } /** Move assignment from AsVariant(). */ - template + template Variant& operator=(detail::AsVariantTemporary&& aValue) { this->~Variant(); @@ -554,6 +573,14 @@ public: return Impl::template tag() == tag; } + template + bool is() const + { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + return N == size_t(tag); + } + /** * Operator == overload that defers to the variant type's operator== * implementation if the rhs is tagged as the same type as this one. @@ -582,6 +609,15 @@ public: return *static_cast(ptr()); } + template + typename detail::Nth::Type& as() + { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return *static_cast::Type*>(ptr()); + } + /** Immutable const reference. */ template const T& as() const { @@ -591,6 +627,15 @@ public: return *static_cast(ptr()); } + template + const typename detail::Nth::Type& as() const + { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return *static_cast::Type*>(ptr()); + } + /** * Extract the contained variant value from this container into a temporary * value. On completion, the value in the variant will be in a @@ -605,6 +650,15 @@ public: return T(Move(as())); } + template + typename detail::Nth::Type extract() + { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return typename detail::Nth::Type(Move(as())); + } + // Exhaustive matching of all variant types on the contained value. /** Match on an immutable const reference. */ diff --git a/mfbt/tests/TestVariant.cpp b/mfbt/tests/TestVariant.cpp index d47df70cb515..3d9ee0c430f4 100644 --- a/mfbt/tests/TestVariant.cpp +++ b/mfbt/tests/TestVariant.cpp @@ -28,6 +28,12 @@ testSimple() MOZ_RELEASE_ASSERT(v.is()); MOZ_RELEASE_ASSERT(!v.is()); MOZ_RELEASE_ASSERT(v.as() == 1); + + MOZ_RELEASE_ASSERT(v.is<1>()); + MOZ_RELEASE_ASSERT(!v.is<0>()); + static_assert(mozilla::IsSame()), uint64_t&>::value, + "as<1>() should return a uint64_t"); + MOZ_RELEASE_ASSERT(v.as<1>() == 1); } static void