/* -*- 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/. */
// 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;
/** * 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: staticconst 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);
}
// 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 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) {}
// 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 { friendstruct IPC::ParamTraits<mozilla::Variant<Ts...>>; friendstruct mozilla::ipc::IPDLParamTraits<mozilla::Variant<Ts...>>;
using Tag = typename detail::VariantTag<Ts...>::Type; using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
// Raw storage for the contained variant value.
alignas(RawDataAlignment) unsignedchar 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; }
constvoid* 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));
}
/** 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;
}
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.
*/ booloperator==(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.
*/ booloperator!=(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> consttypename 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<consttypename 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> consttypename 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<consttypename 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>()); returntypename 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);
}
/** * 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::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 */
¤ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.