mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
81d28b5530
On some systems, uint_fast8_t may be as big as size_t! So the `static_assert(sizeof(aIndex) < sizeof(size_t))` could fail there. The better test here is to check for the expected type (uint_fast8_t). Now, since uint_fast8_t can be bigger than 8 bits, we may as well choose it for variant sizes greater than 255, up to UINT_FAST8_MAX. (The added parentheses help clang-format distinguish '<' for tests vs for templates.) Differential Revision: https://phabricator.services.mozilla.com/D119574
929 lines
33 KiB
C++
929 lines
33 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* A template class for tagged unions. */
|
|
|
|
#include <new>
|
|
#include <stdint.h>
|
|
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/HashFunctions.h"
|
|
#include "mozilla/OperatorNewExtensions.h"
|
|
#include "mozilla/TemplateLib.h"
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#ifndef mozilla_Variant_h
|
|
# define mozilla_Variant_h
|
|
|
|
namespace IPC {
|
|
template <typename T>
|
|
struct ParamTraits;
|
|
} // namespace IPC
|
|
|
|
namespace mozilla {
|
|
|
|
namespace ipc {
|
|
template <typename T>
|
|
struct IPDLParamTraits;
|
|
} // namespace ipc
|
|
|
|
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;
|
|
};
|
|
|
|
/// SelectVariantTypeHelper is used in the implementation of SelectVariantType.
|
|
template <typename T, typename... Variants>
|
|
struct SelectVariantTypeHelper;
|
|
|
|
template <typename T>
|
|
struct SelectVariantTypeHelper<T> {
|
|
static constexpr size_t count = 0;
|
|
};
|
|
|
|
template <typename T, typename... Variants>
|
|
struct SelectVariantTypeHelper<T, T, Variants...> {
|
|
typedef T Type;
|
|
static constexpr size_t count =
|
|
1 + SelectVariantTypeHelper<T, Variants...>::count;
|
|
};
|
|
|
|
template <typename T, typename... Variants>
|
|
struct SelectVariantTypeHelper<T, const T, Variants...> {
|
|
typedef const T Type;
|
|
static constexpr size_t count =
|
|
1 + SelectVariantTypeHelper<T, Variants...>::count;
|
|
};
|
|
|
|
template <typename T, typename... Variants>
|
|
struct SelectVariantTypeHelper<T, const T&, Variants...> {
|
|
typedef const T& Type;
|
|
static constexpr size_t count =
|
|
1 + SelectVariantTypeHelper<T, Variants...>::count;
|
|
};
|
|
|
|
template <typename T, typename... Variants>
|
|
struct SelectVariantTypeHelper<T, T&&, Variants...> {
|
|
typedef T&& Type;
|
|
static constexpr size_t count =
|
|
1 + SelectVariantTypeHelper<T, Variants...>::count;
|
|
};
|
|
|
|
template <typename T, typename Head, typename... Variants>
|
|
struct SelectVariantTypeHelper<T, Head, Variants...>
|
|
: public SelectVariantTypeHelper<T, Variants...> {};
|
|
|
|
/**
|
|
* SelectVariantType takes a type T and a list of variant types Variants and
|
|
* yields a type Type, selected from Variants, that can store a value of type T
|
|
* or a reference to type T. If no such type was found, Type is not defined.
|
|
* SelectVariantType also has a `count` member that contains the total number of
|
|
* selectable types (which will be used to check that a requested type is not
|
|
* ambiguously present twice.)
|
|
*/
|
|
template <typename T, typename... Variants>
|
|
struct SelectVariantType
|
|
: public SelectVariantTypeHelper<
|
|
std::remove_const_t<std::remove_reference_t<T>>, Variants...> {};
|
|
|
|
// Compute a fast, compact type that can be used to hold integral values that
|
|
// distinctly map to every type in Ts.
|
|
template <typename... Ts>
|
|
struct VariantTag {
|
|
private:
|
|
static const size_t TypeCount = sizeof...(Ts);
|
|
|
|
public:
|
|
using Type = std::conditional_t<
|
|
(TypeCount <= 2), bool,
|
|
std::conditional_t<(TypeCount <= size_t(UINT_FAST8_MAX)), uint_fast8_t,
|
|
size_t // stop caring past a certain
|
|
// point :-)
|
|
>>;
|
|
};
|
|
|
|
// TagHelper gets the given sentinel tag value for the given type T. This has to
|
|
// be split out from VariantImplementation because you can't nest a partial
|
|
// template specialization within a template class.
|
|
|
|
template <typename Tag, size_t N, typename T, typename U, typename Next,
|
|
bool isMatch>
|
|
struct TagHelper;
|
|
|
|
// In the case where T != U, we continue recursion.
|
|
template <typename Tag, size_t N, typename T, typename U, typename Next>
|
|
struct TagHelper<Tag, N, T, U, Next, false> {
|
|
static Tag tag() { return Next::template tag<U>(); }
|
|
};
|
|
|
|
// In the case where T == U, return the tag number.
|
|
template <typename Tag, size_t N, typename T, typename U, typename Next>
|
|
struct TagHelper<Tag, N, T, U, Next, true> {
|
|
static Tag tag() { return Tag(N); }
|
|
};
|
|
|
|
// The VariantImplementation template provides the guts of mozilla::Variant. We
|
|
// create a VariantImplementation for each T in Ts... which handles
|
|
// construction, destruction, etc for when the Variant's type is T. If the
|
|
// Variant's type isn't T, it punts the request on to the next
|
|
// VariantImplementation.
|
|
|
|
template <typename Tag, size_t N, typename... Ts>
|
|
struct VariantImplementation;
|
|
|
|
// The singly typed Variant / recursion base case.
|
|
template <typename Tag, size_t N, typename T>
|
|
struct VariantImplementation<Tag, N, T> {
|
|
template <typename U>
|
|
static Tag tag() {
|
|
static_assert(std::is_same_v<T, U>, "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<N>());
|
|
}
|
|
|
|
template <typename Variant>
|
|
static void moveConstruct(void* aLhs, Variant&& aRhs) {
|
|
::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
|
|
}
|
|
|
|
template <typename Variant>
|
|
static void destroy(Variant& aV) {
|
|
aV.template as<N>().~T();
|
|
}
|
|
|
|
template <typename Variant>
|
|
static bool equal(const Variant& aLhs, const Variant& aRhs) {
|
|
return aLhs.template as<N>() == aRhs.template as<N>();
|
|
}
|
|
|
|
template <typename Matcher, typename ConcreteVariant>
|
|
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) {
|
|
if constexpr (std::is_invocable_v<Matcher, Tag,
|
|
decltype(std::forward<ConcreteVariant>(aV)
|
|
.template as<N>())>) {
|
|
return std::forward<Matcher>(aMatcher)(
|
|
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
|
|
} else {
|
|
return std::forward<Matcher>(aMatcher)(
|
|
std::forward<ConcreteVariant>(aV).template as<N>());
|
|
}
|
|
}
|
|
|
|
template <typename ConcreteVariant, typename Matcher>
|
|
static decltype(auto) matchN(ConcreteVariant&& aV, Matcher&& aMatcher) {
|
|
if constexpr (std::is_invocable_v<Matcher, Tag,
|
|
decltype(std::forward<ConcreteVariant>(aV)
|
|
.template as<N>())>) {
|
|
return std::forward<Matcher>(aMatcher)(
|
|
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
|
|
} else {
|
|
return std::forward<Matcher>(aMatcher)(
|
|
std::forward<ConcreteVariant>(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.
|
|
using Next = VariantImplementation<Tag, N + 1, Ts...>;
|
|
|
|
template <typename U>
|
|
static Tag tag() {
|
|
return TagHelper<Tag, N, T, U, Next, std::is_same_v<T, U>>::tag();
|
|
}
|
|
|
|
template <typename Variant>
|
|
static void copyConstruct(void* aLhs, const Variant& aRhs) {
|
|
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<N>()) {
|
|
::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
|
|
} else {
|
|
Next::moveConstruct(aLhs, std::move(aRhs));
|
|
}
|
|
}
|
|
|
|
template <typename Variant>
|
|
static void destroy(Variant& aV) {
|
|
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<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 decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) {
|
|
if (aV.template is<N>()) {
|
|
if constexpr (std::is_invocable_v<Matcher, Tag,
|
|
decltype(std::forward<ConcreteVariant>(
|
|
aV)
|
|
.template as<N>())>) {
|
|
return std::forward<Matcher>(aMatcher)(
|
|
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
|
|
} else {
|
|
return std::forward<Matcher>(aMatcher)(
|
|
std::forward<ConcreteVariant>(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::operator()(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::operator()(T&) overloads are returning
|
|
// different types. They must all return the same type.
|
|
return Next::match(std::forward<Matcher>(aMatcher),
|
|
std::forward<ConcreteVariant>(aV));
|
|
}
|
|
}
|
|
|
|
template <typename ConcreteVariant, typename Mi, typename... Ms>
|
|
static decltype(auto) matchN(ConcreteVariant&& aV, Mi&& aMi, Ms&&... aMs) {
|
|
if (aV.template is<N>()) {
|
|
if constexpr (std::is_invocable_v<Mi, Tag,
|
|
decltype(std::forward<ConcreteVariant>(
|
|
aV)
|
|
.template as<N>())>) {
|
|
static_assert(
|
|
std::is_same_v<
|
|
decltype(std::forward<Mi>(aMi)(
|
|
Tag(N),
|
|
std::forward<ConcreteVariant>(aV).template as<N>())),
|
|
decltype(Next::matchN(std::forward<ConcreteVariant>(aV),
|
|
std::forward<Ms>(aMs)...))>,
|
|
"all matchers must have the same return type");
|
|
return std::forward<Mi>(aMi)(
|
|
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
|
|
} else {
|
|
static_assert(
|
|
std::is_same_v<
|
|
decltype(std::forward<Mi>(aMi)(
|
|
std::forward<ConcreteVariant>(aV).template as<N>())),
|
|
decltype(Next::matchN(std::forward<ConcreteVariant>(aV),
|
|
std::forward<Ms>(aMs)...))>,
|
|
"all matchers must have the same return type");
|
|
return std::forward<Mi>(aMi)(
|
|
std::forward<ConcreteVariant>(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
|
|
// Matchers don't exhaust all variant types. There must exist a
|
|
// Matcher (with its operator()(T&)) for every variant type T, in the
|
|
// exact same order.
|
|
return Next::matchN(std::forward<ConcreteVariant>(aV),
|
|
std::forward<Ms>(aMs)...);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* AsVariantTemporary stores a value of type T to allow construction of a
|
|
* Variant value via type inference. Because T is copied and there's no
|
|
* guarantee that the copy can be elided, AsVariantTemporary is best used with
|
|
* primitive or very small types.
|
|
*/
|
|
template <typename T>
|
|
struct AsVariantTemporary {
|
|
explicit AsVariantTemporary(const T& aValue) : mValue(aValue) {}
|
|
|
|
template <typename U>
|
|
explicit AsVariantTemporary(U&& aValue) : mValue(std::forward<U>(aValue)) {}
|
|
|
|
AsVariantTemporary(const AsVariantTemporary& aOther)
|
|
: mValue(aOther.mValue) {}
|
|
|
|
AsVariantTemporary(AsVariantTemporary&& aOther)
|
|
: mValue(std::move(aOther.mValue)) {}
|
|
|
|
AsVariantTemporary() = delete;
|
|
void operator=(const AsVariantTemporary&) = delete;
|
|
void operator=(AsVariantTemporary&&) = delete;
|
|
|
|
std::remove_const_t<std::remove_reference_t<T>> mValue;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
// Used to unambiguously specify one of the Variant's type.
|
|
template <typename T>
|
|
struct VariantType {
|
|
using Type = T;
|
|
};
|
|
|
|
// Used to specify one of the Variant's type by index.
|
|
template <size_t N>
|
|
struct VariantIndex {
|
|
static constexpr size_t index = N;
|
|
};
|
|
|
|
/**
|
|
* # mozilla::Variant
|
|
*
|
|
* A variant / tagged union / heterogenous disjoint union / sum-type template
|
|
* class. Similar in concept to (but not derived from) `boost::variant`.
|
|
*
|
|
* Sometimes, you may wish to use a C union with non-POD types. However, this is
|
|
* forbidden in C++ because it is not clear which type in the union should have
|
|
* its constructor and destructor run on creation and deletion
|
|
* respectively. This is the problem that `mozilla::Variant` solves.
|
|
*
|
|
* ## Usage
|
|
*
|
|
* A `mozilla::Variant` instance is constructed (via move or copy) from one of
|
|
* its variant types (ignoring const and references). It does *not* support
|
|
* construction from subclasses of variant types or types that coerce to one of
|
|
* the variant types.
|
|
*
|
|
* Variant<char, uint32_t> v1('a');
|
|
* Variant<UniquePtr<A>, B, C> v2(MakeUnique<A>());
|
|
* Variant<bool, char> v3(VariantType<char>, 0); // disambiguation needed
|
|
* Variant<int, int> v4(VariantIndex<1>, 0); // 2nd int
|
|
*
|
|
* Because specifying the full type of a Variant value is often verbose,
|
|
* there are two easier ways to construct values:
|
|
*
|
|
* A. AsVariant() can be used to construct a Variant value using type inference
|
|
* in contexts such as expressions or when returning values from functions.
|
|
* Because AsVariant() must copy or move the value into a temporary and this
|
|
* cannot necessarily be elided by the compiler, it's mostly appropriate only
|
|
* for use with primitive or very small types.
|
|
*
|
|
* Variant<char, uint32_t> Foo() { return AsVariant('x'); }
|
|
* // ...
|
|
* Variant<char, uint32_t> v1 = Foo(); // v1 holds char('x').
|
|
*
|
|
* B. Brace-construction with VariantType or VariantIndex; this also allows
|
|
* in-place construction with any number of arguments.
|
|
*
|
|
* struct AB { AB(int, int){...} };
|
|
* static Variant<AB, bool> foo()
|
|
* {
|
|
* return {VariantIndex<0>{}, 1, 2};
|
|
* }
|
|
* // ...
|
|
* Variant<AB, bool> v0 = Foo(); // v0 holds AB(1,2).
|
|
*
|
|
* 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>()) { // Instead of v.is<B>.
|
|
* ...
|
|
* } else {
|
|
* ...
|
|
* }
|
|
* }
|
|
*
|
|
* In some situation, a Variant may be constructed from templated types, in
|
|
* which case it is possible that the same type could be given multiple times by
|
|
* an external developer. Or seemingly-different types could be aliases.
|
|
* In this case, repeated types can only be accessed through their index, to
|
|
* prevent ambiguous access by type.
|
|
*
|
|
* // Bad!
|
|
* template <typename T>
|
|
* struct ResultOrError
|
|
* {
|
|
* Variant<T, int> m;
|
|
* ResultOrError() : m(int(0)) {} // Error '0' by default
|
|
* ResultOrError(const T& r) : m(r) {}
|
|
* bool IsResult() const { return m.is<T>(); }
|
|
* bool IsError() const { return m.is<int>(); }
|
|
* };
|
|
* // Now instantiante with the result being an int too:
|
|
* ResultOrError<int> myResult(123); // Fail!
|
|
* // In Variant<int, int>, which 'int' are we refering to, from inside
|
|
* // ResultOrError functions?
|
|
*
|
|
* // Good!
|
|
* template <typename T>
|
|
* struct ResultOrError
|
|
* {
|
|
* Variant<T, int> m;
|
|
* ResultOrError() : m(VariantIndex<1>{}, 0) {} // Error '0' by default
|
|
* ResultOrError(const T& r) : m(VariantIndex<0>{}, r) {}
|
|
* bool IsResult() const { return m.is<0>(); } // 0 -> T
|
|
* bool IsError() const { return m.is<1>(); } // 1 -> int
|
|
* };
|
|
* // Now instantiante with the result being an int too:
|
|
* ResultOrError<int> myResult(123); // It now works!
|
|
*
|
|
* Attempting to use the contained value as type `T1` when the `Variant`
|
|
* instance contains a value of type `T2` causes an assertion failure.
|
|
*
|
|
* A a;
|
|
* Variant<A, B, C> v(a);
|
|
* v.as<B>(); // <--- Assertion failure!
|
|
*
|
|
* Trying to use a `Variant<Ts...>` instance as some type `U` that is not a
|
|
* member of the set of `Ts...` is a compiler error.
|
|
*
|
|
* A a;
|
|
* Variant<A, B, C> v(a);
|
|
* v.as<SomeRandomType>(); // <--- Compiler error!
|
|
*
|
|
* 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 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>()) {
|
|
* return ...;
|
|
* } else {
|
|
* return doSomething(v.as<C>()); // Forgot about case D!
|
|
* }
|
|
* }
|
|
*
|
|
* // Instead, a single function object (that can deal with all possible
|
|
* // options) may be provided:
|
|
* struct FooMatcher
|
|
* {
|
|
* // The return type of all matchers must be identical.
|
|
* char* operator()(A& a) { ... }
|
|
* char* operator()(B& b) { ... }
|
|
* char* operator()(C& c) { ... }
|
|
* char* operator()(D& d) { ... } // Compile-time error to forget D!
|
|
* }
|
|
* char* foo(Variant<A, B, C, D>& v) {
|
|
* return v.match(FooMatcher());
|
|
* }
|
|
*
|
|
* // In some situations, a single generic lambda may also be appropriate:
|
|
* char* foo(Variant<A, B, C, D>& v) {
|
|
* return v.match([](auto&) {...});
|
|
* }
|
|
*
|
|
* // Alternatively, multiple function objects may be provided, each one
|
|
* // corresponding to an option, in the same order:
|
|
* char* foo(Variant<A, B, C, D>& v) {
|
|
* return v.match([](A&) { ... },
|
|
* [](B&) { ... },
|
|
* [](C&) { ... },
|
|
* [](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:
|
|
*
|
|
* struct Leaf { };
|
|
*
|
|
* template<typename T>
|
|
* struct Node
|
|
* {
|
|
* T value;
|
|
* Tree<T>* left;
|
|
* Tree<T>* right;
|
|
* };
|
|
*
|
|
* template<typename T>
|
|
* using Tree = Variant<Leaf, Node<T>>;
|
|
*
|
|
* A copy-on-write string is either a non-owning reference to some existing
|
|
* string, or an owning reference to our copy:
|
|
*
|
|
* class CopyOnWriteString
|
|
* {
|
|
* Variant<const char*, UniquePtr<char[]>> string;
|
|
*
|
|
* ...
|
|
* };
|
|
*
|
|
* Because Variant must be aligned suitable to hold any value stored within it,
|
|
* and because |alignas| requirements don't affect platform ABI with respect to
|
|
* how parameters are laid out in memory, Variant can't be used as the type of a
|
|
* function parameter. Pass Variant to functions by pointer or reference
|
|
* instead.
|
|
*/
|
|
template <typename... Ts>
|
|
class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
|
|
friend struct IPC::ParamTraits<mozilla::Variant<Ts...>>;
|
|
friend struct mozilla::ipc::IPDLParamTraits<mozilla::Variant<Ts...>>;
|
|
|
|
using Tag = typename detail::VariantTag<Ts...>::Type;
|
|
using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
|
|
|
|
static constexpr size_t RawDataAlignment = tl::Max<alignof(Ts)...>::value;
|
|
static constexpr size_t RawDataSize = tl::Max<sizeof(Ts)...>::value;
|
|
|
|
// Raw storage for the contained variant value.
|
|
alignas(RawDataAlignment) unsigned char rawData[RawDataSize];
|
|
|
|
// Each type is given a unique tag value that lets us keep track of the
|
|
// contained variant value's type.
|
|
Tag tag;
|
|
|
|
// Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
|
|
// -Werror compile error) to reinterpret_cast<> |rawData| to |T*|, even
|
|
// through |void*|. Placing the latter cast in these separate functions
|
|
// breaks the chain such that affected GCC versions no longer warn/error.
|
|
void* ptr() { return rawData; }
|
|
|
|
const void* ptr() const { return rawData; }
|
|
|
|
public:
|
|
/** Perfect forwarding construction for some variant type T. */
|
|
template <typename RefT,
|
|
// RefT captures both const& as well as && (as intended, to support
|
|
// perfect forwarding), so we have to remove those qualifiers here
|
|
// when ensuring that T is a variant of this type, and getting T's
|
|
// tag, etc.
|
|
typename T = typename detail::SelectVariantType<RefT, Ts...>::Type>
|
|
explicit Variant(RefT&& aT) : tag(Impl::template tag<T>()) {
|
|
static_assert(
|
|
detail::SelectVariantType<RefT, Ts...>::count == 1,
|
|
"Variant can only be selected by type if that type is unique");
|
|
::new (KnownNotNull, ptr()) T(std::forward<RefT>(aT));
|
|
}
|
|
|
|
/**
|
|
* Perfect forwarding construction for some variant type T, by
|
|
* explicitly giving the type.
|
|
* This is necessary to construct from any number of arguments,
|
|
* or to convert from a type that is not in the Variant's type list.
|
|
*/
|
|
template <typename T, typename... Args>
|
|
MOZ_IMPLICIT Variant(const VariantType<T>&, Args&&... aTs)
|
|
: tag(Impl::template tag<T>()) {
|
|
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
|
|
}
|
|
|
|
/**
|
|
* Perfect forwarding construction for some variant type T, by
|
|
* explicitly giving the type index.
|
|
* This is necessary to construct from any number of arguments,
|
|
* or to convert from a type that is not in the Variant's type list,
|
|
* or to construct a type that is present more than once in the Variant.
|
|
*/
|
|
template <size_t N, typename... Args>
|
|
MOZ_IMPLICIT Variant(const VariantIndex<N>&, Args&&... aTs) : tag(N) {
|
|
using T = typename detail::Nth<N, Ts...>::Type;
|
|
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
|
|
}
|
|
|
|
/**
|
|
* Constructs this Variant from an AsVariantTemporary<T> such that T can be
|
|
* stored in one of the types allowable in this Variant. This is used in the
|
|
* implementation of AsVariant().
|
|
*/
|
|
template <typename RefT>
|
|
MOZ_IMPLICIT Variant(detail::AsVariantTemporary<RefT>&& aValue)
|
|
: tag(Impl::template tag<
|
|
typename detail::SelectVariantType<RefT, Ts...>::Type>()) {
|
|
using T = typename detail::SelectVariantType<RefT, Ts...>::Type;
|
|
static_assert(
|
|
detail::SelectVariantType<RefT, Ts...>::count == 1,
|
|
"Variant can only be selected by type if that type is unique");
|
|
::new (KnownNotNull, ptr()) T(std::move(aValue.mValue));
|
|
}
|
|
|
|
/** Copy construction. */
|
|
Variant(const Variant& aRhs) : tag(aRhs.tag) {
|
|
Impl::copyConstruct(ptr(), aRhs);
|
|
}
|
|
|
|
/** Move construction. */
|
|
Variant(Variant&& aRhs) : tag(aRhs.tag) {
|
|
Impl::moveConstruct(ptr(), std::move(aRhs));
|
|
}
|
|
|
|
/** Copy assignment. */
|
|
Variant& operator=(const Variant& aRhs) {
|
|
MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
|
|
this->~Variant();
|
|
::new (KnownNotNull, this) Variant(aRhs);
|
|
return *this;
|
|
}
|
|
|
|
/** Move assignment. */
|
|
Variant& operator=(Variant&& aRhs) {
|
|
MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
|
|
this->~Variant();
|
|
::new (KnownNotNull, this) Variant(std::move(aRhs));
|
|
return *this;
|
|
}
|
|
|
|
/** Move assignment from AsVariant(). */
|
|
template <typename T>
|
|
Variant& operator=(detail::AsVariantTemporary<T>&& aValue) {
|
|
static_assert(
|
|
detail::SelectVariantType<T, Ts...>::count == 1,
|
|
"Variant can only be selected by type if that type is unique");
|
|
this->~Variant();
|
|
::new (KnownNotNull, this) Variant(std::move(aValue));
|
|
return *this;
|
|
}
|
|
|
|
~Variant() { Impl::destroy(*this); }
|
|
|
|
template <typename T, typename... Args>
|
|
T& emplace(Args&&... aTs) {
|
|
Impl::destroy(*this);
|
|
tag = Impl::template tag<T>();
|
|
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
|
|
return as<T>();
|
|
}
|
|
|
|
template <size_t N, typename... Args>
|
|
typename detail::Nth<N, Ts...>::Type& emplace(Args&&... aTs) {
|
|
using T = typename detail::Nth<N, Ts...>::Type;
|
|
Impl::destroy(*this);
|
|
tag = N;
|
|
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
|
|
return as<N>();
|
|
}
|
|
|
|
/** Check which variant type is currently contained. */
|
|
template <typename T>
|
|
bool is() const {
|
|
static_assert(
|
|
detail::SelectVariantType<T, Ts...>::count == 1,
|
|
"provided a type not uniquely 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);
|
|
}
|
|
|
|
/**
|
|
* Operator != overload that defers to the negation of 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 !(*this == aRhs); }
|
|
|
|
// Accessors for working with the contained variant value.
|
|
|
|
/** Mutable lvalue-reference. */
|
|
template <typename T>
|
|
T& as() & {
|
|
static_assert(
|
|
detail::SelectVariantType<T, Ts...>::count == 1,
|
|
"provided a type not uniquely 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 lvalue-reference. */
|
|
template <typename T>
|
|
const T& as() const& {
|
|
static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
|
|
"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());
|
|
}
|
|
|
|
/** Mutable rvalue-reference. */
|
|
template <typename T>
|
|
T&& as() && {
|
|
static_assert(
|
|
detail::SelectVariantType<T, Ts...>::count == 1,
|
|
"provided a type not uniquely found in this Variant's type list");
|
|
MOZ_RELEASE_ASSERT(is<T>());
|
|
return std::move(*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 std::move(
|
|
*static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr()));
|
|
}
|
|
|
|
/** Immutable const rvalue-reference. */
|
|
template <typename T>
|
|
const T&& as() const&& {
|
|
static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
|
|
"provided a type not found in this Variant's type list");
|
|
MOZ_RELEASE_ASSERT(is<T>());
|
|
return std::move(*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 std::move(
|
|
*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::SelectVariantType<T, Ts...>::count == 1,
|
|
"provided a type not uniquely found in this Variant's type list");
|
|
MOZ_ASSERT(is<T>());
|
|
return T(std::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(std::move(as<N>()));
|
|
}
|
|
|
|
// Exhaustive matching of all variant types on the contained value.
|
|
|
|
/** Match on an immutable const lvalue-reference. */
|
|
template <typename Matcher>
|
|
decltype(auto) match(Matcher&& aMatcher) const& {
|
|
return Impl::match(std::forward<Matcher>(aMatcher), *this);
|
|
}
|
|
|
|
template <typename M0, typename M1, typename... Ms>
|
|
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const& {
|
|
return matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
|
|
std::forward<Ms>(aMs)...);
|
|
}
|
|
|
|
/** Match on a mutable non-const lvalue-reference. */
|
|
template <typename Matcher>
|
|
decltype(auto) match(Matcher&& aMatcher) & {
|
|
return Impl::match(std::forward<Matcher>(aMatcher), *this);
|
|
}
|
|
|
|
template <typename M0, typename M1, typename... Ms>
|
|
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) & {
|
|
return matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
|
|
std::forward<Ms>(aMs)...);
|
|
}
|
|
|
|
/** Match on an immutable const rvalue-reference. */
|
|
template <typename Matcher>
|
|
decltype(auto) match(Matcher&& aMatcher) const&& {
|
|
return Impl::match(std::forward<Matcher>(aMatcher), std::move(*this));
|
|
}
|
|
|
|
template <typename M0, typename M1, typename... Ms>
|
|
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const&& {
|
|
return matchN(std::move(*this), std::forward<M0>(aM0),
|
|
std::forward<M1>(aM1), std::forward<Ms>(aMs)...);
|
|
}
|
|
|
|
/** Match on a mutable non-const rvalue-reference. */
|
|
template <typename Matcher>
|
|
decltype(auto) match(Matcher&& aMatcher) && {
|
|
return Impl::match(std::forward<Matcher>(aMatcher), std::move(*this));
|
|
}
|
|
|
|
template <typename M0, typename M1, typename... Ms>
|
|
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) && {
|
|
return matchN(std::move(*this), std::forward<M0>(aM0),
|
|
std::forward<M1>(aM1), std::forward<Ms>(aMs)...);
|
|
}
|
|
|
|
/**
|
|
* Incorporate the current variant's tag into hashValue.
|
|
* Note that this does not hash the actual contents; you must take
|
|
* care of that yourself, perhaps by using a match.
|
|
*/
|
|
mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) const {
|
|
return mozilla::AddToHash(hashValue, tag);
|
|
}
|
|
|
|
private:
|
|
template <typename ConcreteVariant, typename M0, typename M1, typename... Ms>
|
|
static decltype(auto) matchN(ConcreteVariant&& aVariant, M0&& aM0, M1&& aM1,
|
|
Ms&&... aMs) {
|
|
static_assert(
|
|
2 + sizeof...(Ms) == sizeof...(Ts),
|
|
"Variant<T...>::match() takes either one callable argument that "
|
|
"accepts every type T; or one for each type T, in order");
|
|
return Impl::matchN(std::forward<ConcreteVariant>(aVariant),
|
|
std::forward<M0>(aM0), std::forward<M1>(aM1),
|
|
std::forward<Ms>(aMs)...);
|
|
}
|
|
};
|
|
|
|
/*
|
|
* AsVariant() is used to construct a Variant<T,...> value containing the
|
|
* provided T value using type inference. It can be used to construct Variant
|
|
* values in expressions or return them from functions without specifying the
|
|
* entire Variant type.
|
|
*
|
|
* Because AsVariant() must copy or move the value into a temporary and this
|
|
* cannot necessarily be elided by the compiler, it's mostly appropriate only
|
|
* for use with primitive or very small types.
|
|
*
|
|
* AsVariant() returns a AsVariantTemporary value which is implicitly
|
|
* convertible to any Variant that can hold a value of type T.
|
|
*/
|
|
template <typename T>
|
|
detail::AsVariantTemporary<T> AsVariant(T&& aValue) {
|
|
return detail::AsVariantTemporary<T>(std::forward<T>(aValue));
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif /* mozilla_Variant_h */
|