123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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/Alignment.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/Move.h"
- #include "mozilla/TypeTraits.h"
- #ifndef mozilla_Variant_h
- #define mozilla_Variant_h
- namespace mozilla {
- template<typename... Ts>
- class Variant;
- namespace detail {
- // MaxSizeOf computes the maximum sizeof(T) for each T in Ts.
- template<typename T, typename... Ts>
- struct MaxSizeOf
- {
- static const size_t size = sizeof(T) > MaxSizeOf<Ts...>::size
- ? sizeof(T)
- : MaxSizeOf<Ts...>::size;
- };
- template<typename T>
- struct MaxSizeOf<T>
- {
- static const size_t size = sizeof(T);
- };
- // The `IsVariant` helper is used in conjunction with static_assert and
- // `mozilla::EnableIf` to catch passing non-variant types to `Variant::is<T>()`
- // and friends at compile time, rather than at runtime. It ensures that the
- // given type `Needle` is one of the types in the set of types `Haystack`.
- template<typename Needle, typename... Haystack>
- struct IsVariant;
- template<typename Needle>
- struct IsVariant<Needle>
- {
- static const bool value = false;
- };
- template<typename Needle, typename... Haystack>
- struct IsVariant<Needle, Needle, Haystack...>
- {
- static const bool value = true;
- };
- template<typename Needle, typename T, typename... Haystack>
- struct IsVariant<Needle, T, Haystack...> : public IsVariant<Needle, Haystack...> { };
- /// SelectVariantTypeHelper is used in the implementation of SelectVariantType.
- template<typename T, typename... Variants>
- struct SelectVariantTypeHelper;
- template<typename T>
- struct SelectVariantTypeHelper<T>
- { };
- template<typename T, typename... Variants>
- struct SelectVariantTypeHelper<T, T, Variants...>
- {
- typedef T Type;
- };
- template<typename T, typename... Variants>
- struct SelectVariantTypeHelper<T, const T, Variants...>
- {
- typedef const T Type;
- };
- template<typename T, typename... Variants>
- struct SelectVariantTypeHelper<T, const T&, Variants...>
- {
- typedef const T& Type;
- };
- template<typename T, typename... Variants>
- struct SelectVariantTypeHelper<T, T&&, Variants...>
- {
- typedef T&& Type;
- };
- 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.
- */
- template <typename T, typename... Variants>
- struct SelectVariantType
- : public SelectVariantTypeHelper<typename RemoveConst<typename RemoveReference<T>::Type>::Type,
- 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 =
- typename Conditional<TypeCount < 3,
- bool,
- typename Conditional<TypeCount < (1 << 8),
- uint_fast8_t,
- size_t // stop caring past a certain point :-)
- >::Type
- >::Type;
- };
- // 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(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 (aLhs) T(aRhs.template as<T>());
- }
- template<typename Variant>
- static void moveConstruct(void* aLhs, Variant&& aRhs) {
- new (aLhs) T(aRhs.template extract<T>());
- }
- template<typename Variant>
- static void destroy(Variant& aV) {
- aV.template as<T>().~T();
- }
- template<typename Variant>
- static bool
- equal(const Variant& aLhs, const Variant& aRhs) {
- return aLhs.template as<T>() == aRhs.template as<T>();
- }
- template<typename Matcher, typename ConcreteVariant>
- static auto
- match(Matcher&& aMatcher, ConcreteVariant& aV)
- -> decltype(aMatcher.match(aV.template as<T>()))
- {
- return aMatcher.match(aV.template as<T>());
- }
- };
- // 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, IsSame<T, U>::value>::tag();
- }
- template<typename Variant>
- static void copyConstruct(void* aLhs, const Variant& aRhs) {
- if (aRhs.template is<T>()) {
- new (aLhs) T(aRhs.template as<T>());
- } else {
- Next::copyConstruct(aLhs, aRhs);
- }
- }
- template<typename Variant>
- static void moveConstruct(void* aLhs, Variant&& aRhs) {
- if (aRhs.template is<T>()) {
- new (aLhs) T(aRhs.template extract<T>());
- } else {
- Next::moveConstruct(aLhs, aRhs);
- }
- }
- template<typename Variant>
- static void destroy(Variant& aV) {
- if (aV.template is<T>()) {
- aV.template as<T>().~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>();
- } 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>()))
- {
- 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);
- }
- }
- };
- /**
- * 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(Forward<U>(aValue))
- {}
- AsVariantTemporary(const AsVariantTemporary& aOther)
- : mValue(aOther.mValue)
- {}
- AsVariantTemporary(AsVariantTemporary&& aOther)
- : mValue(Move(aOther.mValue))
- {}
- AsVariantTemporary() = delete;
- void operator=(const AsVariantTemporary&) = delete;
- void operator=(AsVariantTemporary&&) = delete;
- typename RemoveConst<typename RemoveReference<T>::Type>::Type mValue;
- };
- } // namespace detail
- /**
- * # 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>());
- *
- * Because specifying the full type of a Variant value is often verbose,
- * 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').
- *
- * All access to the contained value goes through type-safe accessors.
- *
- * void
- * Foo(Variant<A, B, C> v)
- * {
- * if (v.is<A>()) {
- * A& ref = v.as<A>();
- * ...
- * } else {
- * ...
- * }
- * }
- *
- * 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 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
- * {
- * // The return type of all matchers must be identical.
- * char* match(A& a) { ... }
- * char* match(B& b) { ... }
- * char* match(C& c) { ... }
- * char* 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:
- *
- * 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;
- *
- * ...
- * };
- */
- template<typename... Ts>
- class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Variant
- {
- using Tag = typename detail::VariantTag<Ts...>::Type;
- using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
- using RawData = AlignedStorage<detail::MaxSizeOf<Ts...>::size>;
- // Raw storage for the contained variant value.
- RawData raw;
- // Each type is given a unique tag value that lets us keep track of the
- // contained variant value's type.
- Tag tag;
- void* ptr() {
- return reinterpret_cast<void*>(&raw);
- }
- 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>())
- {
- new (ptr()) T(Forward<RefT>(aT));
- }
- /**
- * 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,
- typename T = typename detail::SelectVariantType<RefT, Ts...>::Type>
- MOZ_IMPLICIT Variant(detail::AsVariantTemporary<RefT>&& aValue)
- : tag(Impl::template tag<T>())
- {
- new (ptr()) T(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(), Move(aRhs));
- }
- /** Copy assignment. */
- Variant& operator=(const Variant& aRhs) {
- MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
- this->~Variant();
- new (this) Variant(aRhs);
- return *this;
- }
- /** Move assignment. */
- Variant& operator=(Variant&& aRhs) {
- MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
- this->~Variant();
- new (this) Variant(Move(aRhs));
- return *this;
- }
- /** Move assignment from AsVariant(). */
- template <typename T>
- Variant& operator=(detail::AsVariantTemporary<T>&& aValue)
- {
- this->~Variant();
- new (this) Variant(Move(aValue));
- return *this;
- }
- ~Variant()
- {
- Impl::destroy(*this);
- }
- /** 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;
- }
- /**
- * 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 reference. */
- template<typename T>
- T& as() {
- static_assert(detail::IsVariant<T, Ts...>::value,
- "provided a type not found in this Variant's type list");
- MOZ_ASSERT(is<T>());
- return *reinterpret_cast<T*>(&raw);
- }
- /** 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_ASSERT(is<T>());
- return *reinterpret_cast<const T*>(&raw);
- }
- /**
- * 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>()));
- }
- // 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))
- {
- return Impl::match(aMatcher, *this);
- }
- /** Match on a mutable non-const reference. */
- template<typename Matcher>
- auto
- match(Matcher&& aMatcher)
- -> decltype(Impl::match(aMatcher, *this))
- {
- return Impl::match(aMatcher, *this);
- }
- };
- /*
- * 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>(Forward<T>(aValue));
- }
- } // namespace mozilla
- #endif /* mozilla_Variant_h */
|