--- a/mfbt/Variant.h
+++ b/mfbt/Variant.h
@@ -20,16 +20,32 @@
namespace mozilla {
template<typename... Ts>
class Variant;
namespace detail {
+// Nth<N, types...>::Type is the Nth type (0-based) in the list of types Ts.
+template<size_t N, typename... Ts>
+struct Nth;
+
+template<typename T, typename... Ts>
+struct Nth<0, T, Ts...>
+{
+ using Type = T;
+};
+
+template<size_t N, typename T, typename... Ts>
+struct Nth<N, T, Ts...>
+{
+ using Type = typename Nth<N - 1, Ts...>::Type;
+};
+
template <typename...>
struct FirstTypeIsInRest;
template <typename First>
struct FirstTypeIsInRest<First> : FalseType {};
template <typename First, typename Second, typename... Rest>
struct FirstTypeIsInRest<First, Second, Rest...>
@@ -175,41 +191,41 @@ struct VariantImplementation<Tag, N, T>
static Tag tag() {
static_assert(mozilla::IsSame<T, U>::value,
"mozilla::Variant: tag: bad type!");
return Tag(N);
}
template<typename Variant>
static void copyConstruct(void* aLhs, const Variant& aRhs) {
- ::new (KnownNotNull, aLhs) T(aRhs.template as<T>());
+ ::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
}
template<typename Variant>
static void moveConstruct(void* aLhs, Variant&& aRhs) {
- ::new (KnownNotNull, aLhs) T(aRhs.template extract<T>());
+ ::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
}
template<typename Variant>
static void destroy(Variant& aV) {
- aV.template as<T>().~T();
+ aV.template as<N>().~T();
}
template<typename Variant>
static bool
equal(const Variant& aLhs, const Variant& aRhs) {
- return aLhs.template as<T>() == aRhs.template as<T>();
+ return aLhs.template as<N>() == aRhs.template as<N>();
}
template<typename Matcher, typename ConcreteVariant>
static auto
match(Matcher&& aMatcher, ConcreteVariant& aV)
- -> decltype(aMatcher.match(aV.template as<T>()))
+ -> decltype(aMatcher.match(aV.template as<N>()))
{
- return aMatcher.match(aV.template as<T>());
+ return aMatcher.match(aV.template as<N>());
}
};
// VariantImplementation for some variant type T.
template<typename Tag, size_t N, typename T, typename... Ts>
struct VariantImplementation<Tag, N, T, Ts...>
{
// The next recursive VariantImplementation.
@@ -217,58 +233,58 @@ struct VariantImplementation<Tag, N, T,
template<typename U>
static Tag tag() {
return TagHelper<Tag, N, T, U, Next, IsSame<T, U>::value>::tag();
}
template<typename Variant>
static void copyConstruct(void* aLhs, const Variant& aRhs) {
- if (aRhs.template is<T>()) {
- ::new (KnownNotNull, aLhs) T(aRhs.template as<T>());
+ if (aRhs.template is<N>()) {
+ ::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
} else {
Next::copyConstruct(aLhs, aRhs);
}
}
template<typename Variant>
static void moveConstruct(void* aLhs, Variant&& aRhs) {
- if (aRhs.template is<T>()) {
- ::new (KnownNotNull, aLhs) T(aRhs.template extract<T>());
+ if (aRhs.template is<N>()) {
+ ::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
} else {
Next::moveConstruct(aLhs, Move(aRhs));
}
}
template<typename Variant>
static void destroy(Variant& aV) {
- if (aV.template is<T>()) {
- aV.template as<T>().~T();
+ if (aV.template is<N>()) {
+ aV.template as<N>().~T();
} else {
Next::destroy(aV);
}
}
template<typename Variant>
static bool equal(const Variant& aLhs, const Variant& aRhs) {
- if (aLhs.template is<T>()) {
- MOZ_ASSERT(aRhs.template is<T>());
- return aLhs.template as<T>() == aRhs.template as<T>();
+ if (aLhs.template is<N>()) {
+ MOZ_ASSERT(aRhs.template is<N>());
+ return aLhs.template as<N>() == aRhs.template as<N>();
} else {
return Next::equal(aLhs, aRhs);
}
}
template<typename Matcher, typename ConcreteVariant>
static auto
match(Matcher&& aMatcher, ConcreteVariant& aV)
- -> decltype(aMatcher.match(aV.template as<T>()))
+ -> decltype(aMatcher.match(aV.template as<N>()))
{
- if (aV.template is<T>()) {
- return aMatcher.match(aV.template as<T>());
+ if (aV.template is<N>()) {
+ return aMatcher.match(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
// 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
@@ -344,23 +360,26 @@ struct AsVariantTemporary
* with primitive or very small types.
*
*
* Variant<char, uint32_t> Foo() { return AsVariant('x'); }
* // ...
* Variant<char, uint32_t> 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<A, B, C> v)
* {
* if (v.is<A>()) {
* A& ref = v.as<A>();
* ...
+ * } else (v.is<1>()) { // Same as v.is<B> in this case.
+ * ...
* } else {
* ...
* }
* }
*
* Attempting to use the contained value as type `T1` when the `Variant`
* instance contains a value of type `T2` causes an assertion failure.
*
@@ -377,18 +396,18 @@ struct AsVariantTemporary
*
* Additionally, you can turn a `Variant` that `is<T>` into a `T` by moving it
* out of the containing `Variant` instance with the `extract<T>` method:
*
* 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
+ * different code paths depending on 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>()) {
@@ -528,17 +547,17 @@ public:
Variant& operator=(Variant&& aRhs) {
MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
this->~Variant();
::new (KnownNotNull, this) Variant(Move(aRhs));
return *this;
}
/** Move assignment from AsVariant(). */
- template <typename T>
+ template<typename T>
Variant& operator=(detail::AsVariantTemporary<T>&& aValue)
{
this->~Variant();
::new (KnownNotNull, this) Variant(Move(aValue));
return *this;
}
~Variant()
@@ -549,16 +568,24 @@ public:
/** Check which variant type is currently contained. */
template<typename T>
bool is() const {
static_assert(detail::IsVariant<T, Ts...>::value,
"provided a type not found in this Variant's type list");
return Impl::template tag<T>() == tag;
}
+ template<size_t N>
+ 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.
*/
bool operator==(const Variant& aRhs) const {
return tag == aRhs.tag && Impl::equal(*this, aRhs);
}
@@ -577,39 +604,66 @@ public:
template<typename T>
T& as() {
static_assert(detail::IsVariant<T, Ts...>::value,
"provided a type not found in this Variant's type list");
MOZ_RELEASE_ASSERT(is<T>());
return *static_cast<T*>(ptr());
}
+ template<size_t N>
+ typename detail::Nth<N, Ts...>::Type& as()
+ {
+ static_assert(N < sizeof...(Ts),
+ "provided an index outside of this Variant's type list");
+ MOZ_RELEASE_ASSERT(is<N>());
+ return *static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr());
+ }
+
/** Immutable const reference. */
template<typename T>
const T& as() const {
static_assert(detail::IsVariant<T, Ts...>::value,
"provided a type not found in this Variant's type list");
MOZ_RELEASE_ASSERT(is<T>());
return *static_cast<const T*>(ptr());
}
+ template<size_t N>
+ const typename detail::Nth<N, Ts...>::Type& as() const
+ {
+ static_assert(N < sizeof...(Ts),
+ "provided an index outside of this Variant's type list");
+ MOZ_RELEASE_ASSERT(is<N>());
+ return *static_cast<const typename detail::Nth<N, Ts...>::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
* safely-destructible state, as determined by the behavior of T's move
* constructor when provided the variant's internal value.
*/
template<typename T>
T extract() {
static_assert(detail::IsVariant<T, Ts...>::value,
"provided a type not found in this Variant's type list");
MOZ_ASSERT(is<T>());
return T(Move(as<T>()));
}
+ template<size_t N>
+ typename detail::Nth<N, Ts...>::Type extract()
+ {
+ static_assert(N < sizeof...(Ts),
+ "provided an index outside of this Variant's type list");
+ MOZ_RELEASE_ASSERT(is<N>());
+ return typename detail::Nth<N, Ts...>::Type(Move(as<N>()));
+ }
+
// Exhaustive matching of all variant types on the contained value.
/** Match on an immutable const reference. */
template<typename Matcher>
auto
match(Matcher&& aMatcher) const
-> decltype(Impl::match(aMatcher, *this))
{