Bug 1338389 - Index-based Variant::is<N>, as<N>, and extract<N> - r=froydnj

MozReview-Commit-ID: C5iga0Eb1tH

--HG--
extra : rebase_source : d88cf614318cc8544d7ab52315e015a7ca4e5efd
This commit is contained in:
Gerald Squelart 2017-05-08 11:09:21 +12:00
Родитель fd1e018417
Коммит 6015fbb5ac
2 изменённых файлов: 81 добавлений и 21 удалений

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

@ -25,6 +25,22 @@ 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;
@ -180,31 +196,31 @@ struct VariantImplementation<Tag, N, T>
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>());
}
};
@ -222,8 +238,8 @@ struct VariantImplementation<Tag, N, T, Ts...>
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);
}
@ -231,8 +247,8 @@ struct VariantImplementation<Tag, N, T, Ts...>
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));
}
@ -240,8 +256,8 @@ struct VariantImplementation<Tag, N, T, Ts...>
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);
}
@ -249,9 +265,9 @@ struct VariantImplementation<Tag, N, T, Ts...>
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);
}
@ -260,10 +276,10 @@ struct VariantImplementation<Tag, N, T, Ts...>
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
@ -349,6 +365,7 @@ struct AsVariantTemporary
* 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)
@ -356,6 +373,8 @@ struct AsVariantTemporary
* if (v.is<A>()) {
* A& ref = v.as<A>();
* ...
* } else (v.is<1>()) { // Same as v.is<B> in this case.
* ...
* } else {
* ...
* }
@ -382,8 +401,8 @@ struct AsVariantTemporary
* 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.
*
@ -533,7 +552,7 @@ public:
}
/** Move assignment from AsVariant(). */
template <typename T>
template<typename T>
Variant& operator=(detail::AsVariantTemporary<T>&& aValue)
{
this->~Variant();
@ -554,6 +573,14 @@ public:
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.
@ -582,6 +609,15 @@ public:
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 {
@ -591,6 +627,15 @@ public:
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
@ -605,6 +650,15 @@ public:
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. */

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

@ -28,6 +28,12 @@ testSimple()
MOZ_RELEASE_ASSERT(v.is<uint64_t>());
MOZ_RELEASE_ASSERT(!v.is<uint32_t>());
MOZ_RELEASE_ASSERT(v.as<uint64_t>() == 1);
MOZ_RELEASE_ASSERT(v.is<1>());
MOZ_RELEASE_ASSERT(!v.is<0>());
static_assert(mozilla::IsSame<decltype(v.as<1>()), uint64_t&>::value,
"as<1>() should return a uint64_t");
MOZ_RELEASE_ASSERT(v.as<1>() == 1);
}
static void