no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / mfbt / Variant.h
blobd1db3a2cc92584667aab03843ceb62cdcab52d24
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A template class for tagged unions. */
9 #include <new>
10 #include <stdint.h>
12 #include "mozilla/Assertions.h"
13 #include "mozilla/HashFunctions.h"
14 #include "mozilla/OperatorNewExtensions.h"
15 #include "mozilla/TemplateLib.h"
16 #include <type_traits>
17 #include <utility>
19 #ifndef mozilla_Variant_h
20 # define mozilla_Variant_h
22 namespace IPC {
23 template <typename T>
24 struct ParamTraits;
25 } // namespace IPC
27 namespace mozilla {
29 namespace ipc {
30 template <typename T>
31 struct IPDLParamTraits;
32 } // namespace ipc
34 template <typename... Ts>
35 class Variant;
37 namespace detail {
39 // Nth<N, types...>::Type is the Nth type (0-based) in the list of types Ts.
40 template <size_t N, typename... Ts>
41 struct Nth;
43 template <typename T, typename... Ts>
44 struct Nth<0, T, Ts...> {
45 using Type = T;
48 template <size_t N, typename T, typename... Ts>
49 struct Nth<N, T, Ts...> {
50 using Type = typename Nth<N - 1, Ts...>::Type;
53 /// SelectVariantTypeHelper is used in the implementation of SelectVariantType.
54 template <typename T, typename... Variants>
55 struct SelectVariantTypeHelper;
57 template <typename T>
58 struct SelectVariantTypeHelper<T> {
59 static constexpr size_t count = 0;
62 template <typename T, typename... Variants>
63 struct SelectVariantTypeHelper<T, T, Variants...> {
64 typedef T Type;
65 static constexpr size_t count =
66 1 + SelectVariantTypeHelper<T, Variants...>::count;
69 template <typename T, typename... Variants>
70 struct SelectVariantTypeHelper<T, const T, Variants...> {
71 typedef const T Type;
72 static constexpr size_t count =
73 1 + SelectVariantTypeHelper<T, Variants...>::count;
76 template <typename T, typename... Variants>
77 struct SelectVariantTypeHelper<T, const T&, Variants...> {
78 typedef const T& Type;
79 static constexpr size_t count =
80 1 + SelectVariantTypeHelper<T, Variants...>::count;
83 template <typename T, typename... Variants>
84 struct SelectVariantTypeHelper<T, T&&, Variants...> {
85 typedef T&& Type;
86 static constexpr size_t count =
87 1 + SelectVariantTypeHelper<T, Variants...>::count;
90 template <typename T, typename Head, typename... Variants>
91 struct SelectVariantTypeHelper<T, Head, Variants...>
92 : public SelectVariantTypeHelper<T, Variants...> {};
94 /**
95 * SelectVariantType takes a type T and a list of variant types Variants and
96 * yields a type Type, selected from Variants, that can store a value of type T
97 * or a reference to type T. If no such type was found, Type is not defined.
98 * SelectVariantType also has a `count` member that contains the total number of
99 * selectable types (which will be used to check that a requested type is not
100 * ambiguously present twice.)
102 template <typename T, typename... Variants>
103 struct SelectVariantType
104 : public SelectVariantTypeHelper<
105 std::remove_const_t<std::remove_reference_t<T>>, Variants...> {};
107 // Compute a fast, compact type that can be used to hold integral values that
108 // distinctly map to every type in Ts.
109 template <typename... Ts>
110 struct VariantTag {
111 private:
112 static const size_t TypeCount = sizeof...(Ts);
114 public:
115 using Type = std::conditional_t<
116 (TypeCount <= 2), bool,
117 std::conditional_t<(TypeCount <= size_t(UINT_FAST8_MAX)), uint_fast8_t,
118 size_t // stop caring past a certain
119 // point :-)
123 // TagHelper gets the given sentinel tag value for the given type T. This has to
124 // be split out from VariantImplementation because you can't nest a partial
125 // template specialization within a template class.
127 template <typename Tag, size_t N, typename T, typename U, typename Next,
128 bool isMatch>
129 struct TagHelper;
131 // In the case where T != U, we continue recursion.
132 template <typename Tag, size_t N, typename T, typename U, typename Next>
133 struct TagHelper<Tag, N, T, U, Next, false> {
134 static Tag tag() { return Next::template tag<U>(); }
137 // In the case where T == U, return the tag number.
138 template <typename Tag, size_t N, typename T, typename U, typename Next>
139 struct TagHelper<Tag, N, T, U, Next, true> {
140 static Tag tag() { return Tag(N); }
143 // The VariantImplementation template provides the guts of mozilla::Variant. We
144 // create a VariantImplementation for each T in Ts... which handles
145 // construction, destruction, etc for when the Variant's type is T. If the
146 // Variant's type isn't T, it punts the request on to the next
147 // VariantImplementation.
149 template <typename Tag, size_t N, typename... Ts>
150 struct VariantImplementation;
152 // The singly typed Variant / recursion base case.
153 template <typename Tag, size_t N, typename T>
154 struct VariantImplementation<Tag, N, T> {
155 template <typename U>
156 static Tag tag() {
157 static_assert(std::is_same_v<T, U>, "mozilla::Variant: tag: bad type!");
158 return Tag(N);
161 template <typename Variant>
162 static void copyConstruct(void* aLhs, const Variant& aRhs) {
163 ::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
166 template <typename Variant>
167 static void moveConstruct(void* aLhs, Variant&& aRhs) {
168 ::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
171 template <typename Variant>
172 static void destroy(Variant& aV) {
173 aV.template as<N>().~T();
176 template <typename Variant>
177 static bool equal(const Variant& aLhs, const Variant& aRhs) {
178 return aLhs.template as<N>() == aRhs.template as<N>();
181 template <typename Matcher, typename ConcreteVariant>
182 static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) {
183 if constexpr (std::is_invocable_v<Matcher, Tag,
184 decltype(std::forward<ConcreteVariant>(aV)
185 .template as<N>())>) {
186 return std::forward<Matcher>(aMatcher)(
187 Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
188 } else {
189 return std::forward<Matcher>(aMatcher)(
190 std::forward<ConcreteVariant>(aV).template as<N>());
194 template <typename ConcreteVariant, typename Matcher>
195 static decltype(auto) matchN(ConcreteVariant&& aV, Matcher&& aMatcher) {
196 if constexpr (std::is_invocable_v<Matcher, Tag,
197 decltype(std::forward<ConcreteVariant>(aV)
198 .template as<N>())>) {
199 return std::forward<Matcher>(aMatcher)(
200 Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
201 } else {
202 return std::forward<Matcher>(aMatcher)(
203 std::forward<ConcreteVariant>(aV).template as<N>());
208 // VariantImplementation for some variant type T.
209 template <typename Tag, size_t N, typename T, typename... Ts>
210 struct VariantImplementation<Tag, N, T, Ts...> {
211 // The next recursive VariantImplementation.
212 using Next = VariantImplementation<Tag, N + 1, Ts...>;
214 template <typename U>
215 static Tag tag() {
216 return TagHelper<Tag, N, T, U, Next, std::is_same_v<T, U>>::tag();
219 template <typename Variant>
220 static void copyConstruct(void* aLhs, const Variant& aRhs) {
221 if (aRhs.template is<N>()) {
222 ::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
223 } else {
224 Next::copyConstruct(aLhs, aRhs);
228 template <typename Variant>
229 static void moveConstruct(void* aLhs, Variant&& aRhs) {
230 if (aRhs.template is<N>()) {
231 ::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
232 } else {
233 Next::moveConstruct(aLhs, std::move(aRhs));
237 template <typename Variant>
238 static void destroy(Variant& aV) {
239 if (aV.template is<N>()) {
240 aV.template as<N>().~T();
241 } else {
242 Next::destroy(aV);
246 template <typename Variant>
247 static bool equal(const Variant& aLhs, const Variant& aRhs) {
248 if (aLhs.template is<N>()) {
249 MOZ_ASSERT(aRhs.template is<N>());
250 return aLhs.template as<N>() == aRhs.template as<N>();
251 } else {
252 return Next::equal(aLhs, aRhs);
256 template <typename Matcher, typename ConcreteVariant>
257 static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) {
258 if (aV.template is<N>()) {
259 if constexpr (std::is_invocable_v<Matcher, Tag,
260 decltype(std::forward<ConcreteVariant>(
262 .template as<N>())>) {
263 return std::forward<Matcher>(aMatcher)(
264 Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
265 } else {
266 return std::forward<Matcher>(aMatcher)(
267 std::forward<ConcreteVariant>(aV).template as<N>());
269 } else {
270 // If you're seeing compilation errors here like "no matching
271 // function for call to 'match'" then that means that the
272 // Matcher doesn't exhaust all variant types. There must exist a
273 // Matcher::operator()(T&) for every variant type T.
275 // If you're seeing compilation errors here like "cannot initialize
276 // return object of type <...> with an rvalue of type <...>" then that
277 // means that the Matcher::operator()(T&) overloads are returning
278 // different types. They must all return the same type.
279 return Next::match(std::forward<Matcher>(aMatcher),
280 std::forward<ConcreteVariant>(aV));
284 template <typename ConcreteVariant, typename Mi, typename... Ms>
285 static decltype(auto) matchN(ConcreteVariant&& aV, Mi&& aMi, Ms&&... aMs) {
286 if (aV.template is<N>()) {
287 if constexpr (std::is_invocable_v<Mi, Tag,
288 decltype(std::forward<ConcreteVariant>(
290 .template as<N>())>) {
291 static_assert(
292 std::is_same_v<
293 decltype(std::forward<Mi>(aMi)(
294 Tag(N),
295 std::forward<ConcreteVariant>(aV).template as<N>())),
296 decltype(Next::matchN(std::forward<ConcreteVariant>(aV),
297 std::forward<Ms>(aMs)...))>,
298 "all matchers must have the same return type");
299 return std::forward<Mi>(aMi)(
300 Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
301 } else {
302 static_assert(
303 std::is_same_v<
304 decltype(std::forward<Mi>(aMi)(
305 std::forward<ConcreteVariant>(aV).template as<N>())),
306 decltype(Next::matchN(std::forward<ConcreteVariant>(aV),
307 std::forward<Ms>(aMs)...))>,
308 "all matchers must have the same return type");
309 return std::forward<Mi>(aMi)(
310 std::forward<ConcreteVariant>(aV).template as<N>());
312 } else {
313 // If you're seeing compilation errors here like "no matching
314 // function for call to 'match'" then that means that the
315 // Matchers don't exhaust all variant types. There must exist a
316 // Matcher (with its operator()(T&)) for every variant type T, in the
317 // exact same order.
318 return Next::matchN(std::forward<ConcreteVariant>(aV),
319 std::forward<Ms>(aMs)...);
325 * AsVariantTemporary stores a value of type T to allow construction of a
326 * Variant value via type inference. Because T is copied and there's no
327 * guarantee that the copy can be elided, AsVariantTemporary is best used with
328 * primitive or very small types.
330 template <typename T>
331 struct AsVariantTemporary {
332 explicit AsVariantTemporary(const T& aValue) : mValue(aValue) {}
334 template <typename U>
335 explicit AsVariantTemporary(U&& aValue) : mValue(std::forward<U>(aValue)) {}
337 AsVariantTemporary(const AsVariantTemporary& aOther)
338 : mValue(aOther.mValue) {}
340 AsVariantTemporary(AsVariantTemporary&& aOther)
341 : mValue(std::move(aOther.mValue)) {}
343 AsVariantTemporary() = delete;
344 void operator=(const AsVariantTemporary&) = delete;
345 void operator=(AsVariantTemporary&&) = delete;
347 std::remove_const_t<std::remove_reference_t<T>> mValue;
350 } // namespace detail
352 // Used to unambiguously specify one of the Variant's type.
353 template <typename T>
354 struct VariantType {
355 using Type = T;
358 // Used to specify one of the Variant's type by index.
359 template <size_t N>
360 struct VariantIndex {
361 static constexpr size_t index = N;
365 * # mozilla::Variant
367 * A variant / tagged union / heterogenous disjoint union / sum-type template
368 * class. Similar in concept to (but not derived from) `boost::variant`.
370 * Sometimes, you may wish to use a C union with non-POD types. However, this is
371 * forbidden in C++ because it is not clear which type in the union should have
372 * its constructor and destructor run on creation and deletion
373 * respectively. This is the problem that `mozilla::Variant` solves.
375 * ## Usage
377 * A `mozilla::Variant` instance is constructed (via move or copy) from one of
378 * its variant types (ignoring const and references). It does *not* support
379 * construction from subclasses of variant types or types that coerce to one of
380 * the variant types.
382 * Variant<char, uint32_t> v1('a');
383 * Variant<UniquePtr<A>, B, C> v2(MakeUnique<A>());
384 * Variant<bool, char> v3(VariantType<char>, 0); // disambiguation needed
385 * Variant<int, int> v4(VariantIndex<1>, 0); // 2nd int
387 * Because specifying the full type of a Variant value is often verbose,
388 * there are two easier ways to construct values:
390 * A. AsVariant() can be used to construct a Variant value using type inference
391 * in contexts such as expressions or when returning values from functions.
392 * Because AsVariant() must copy or move the value into a temporary and this
393 * cannot necessarily be elided by the compiler, it's mostly appropriate only
394 * for use with primitive or very small types.
396 * Variant<char, uint32_t> Foo() { return AsVariant('x'); }
397 * // ...
398 * Variant<char, uint32_t> v1 = Foo(); // v1 holds char('x').
400 * B. Brace-construction with VariantType or VariantIndex; this also allows
401 * in-place construction with any number of arguments.
403 * struct AB { AB(int, int){...} };
404 * static Variant<AB, bool> foo()
406 * return {VariantIndex<0>{}, 1, 2};
408 * // ...
409 * Variant<AB, bool> v0 = Foo(); // v0 holds AB(1,2).
411 * All access to the contained value goes through type-safe accessors.
412 * Either the stored type, or the type index may be provided.
414 * void
415 * Foo(Variant<A, B, C> v)
417 * if (v.is<A>()) {
418 * A& ref = v.as<A>();
419 * ...
420 * } else (v.is<1>()) { // Instead of v.is<B>.
421 * ...
422 * } else {
423 * ...
427 * In some situation, a Variant may be constructed from templated types, in
428 * which case it is possible that the same type could be given multiple times by
429 * an external developer. Or seemingly-different types could be aliases.
430 * In this case, repeated types can only be accessed through their index, to
431 * prevent ambiguous access by type.
433 * // Bad!
434 * template <typename T>
435 * struct ResultOrError
437 * Variant<T, int> m;
438 * ResultOrError() : m(int(0)) {} // Error '0' by default
439 * ResultOrError(const T& r) : m(r) {}
440 * bool IsResult() const { return m.is<T>(); }
441 * bool IsError() const { return m.is<int>(); }
442 * };
443 * // Now instantiante with the result being an int too:
444 * ResultOrError<int> myResult(123); // Fail!
445 * // In Variant<int, int>, which 'int' are we refering to, from inside
446 * // ResultOrError functions?
448 * // Good!
449 * template <typename T>
450 * struct ResultOrError
452 * Variant<T, int> m;
453 * ResultOrError() : m(VariantIndex<1>{}, 0) {} // Error '0' by default
454 * ResultOrError(const T& r) : m(VariantIndex<0>{}, r) {}
455 * bool IsResult() const { return m.is<0>(); } // 0 -> T
456 * bool IsError() const { return m.is<1>(); } // 1 -> int
457 * };
458 * // Now instantiante with the result being an int too:
459 * ResultOrError<int> myResult(123); // It now works!
461 * Attempting to use the contained value as type `T1` when the `Variant`
462 * instance contains a value of type `T2` causes an assertion failure.
464 * A a;
465 * Variant<A, B, C> v(a);
466 * v.as<B>(); // <--- Assertion failure!
468 * Trying to use a `Variant<Ts...>` instance as some type `U` that is not a
469 * member of the set of `Ts...` is a compiler error.
471 * A a;
472 * Variant<A, B, C> v(a);
473 * v.as<SomeRandomType>(); // <--- Compiler error!
475 * Additionally, you can turn a `Variant` that `is<T>` into a `T` by moving it
476 * out of the containing `Variant` instance with the `extract<T>` method:
478 * Variant<UniquePtr<A>, B, C> v(MakeUnique<A>());
479 * auto ptr = v.extract<UniquePtr<A>>();
481 * Finally, you can exhaustively match on the contained variant and branch into
482 * different code paths depending on which type is contained. This is preferred
483 * to manually checking every variant type T with is<T>() because it provides
484 * compile-time checking that you handled every type, rather than runtime
485 * assertion failures.
487 * // Bad!
488 * char* foo(Variant<A, B, C, D>& v) {
489 * if (v.is<A>()) {
490 * return ...;
491 * } else if (v.is<B>()) {
492 * return ...;
493 * } else {
494 * return doSomething(v.as<C>()); // Forgot about case D!
498 * // Instead, a single function object (that can deal with all possible
499 * // options) may be provided:
500 * struct FooMatcher
502 * // The return type of all matchers must be identical.
503 * char* operator()(A& a) { ... }
504 * char* operator()(B& b) { ... }
505 * char* operator()(C& c) { ... }
506 * char* operator()(D& d) { ... } // Compile-time error to forget D!
508 * char* foo(Variant<A, B, C, D>& v) {
509 * return v.match(FooMatcher());
512 * // In some situations, a single generic lambda may also be appropriate:
513 * char* foo(Variant<A, B, C, D>& v) {
514 * return v.match([](auto&) {...});
517 * // Alternatively, multiple function objects may be provided, each one
518 * // corresponding to an option, in the same order:
519 * char* foo(Variant<A, B, C, D>& v) {
520 * return v.match([](A&) { ... },
521 * [](B&) { ... },
522 * [](C&) { ... },
523 * [](D&) { ... });
526 * // In rare cases, the index of the currently-active alternative is
527 * // needed, it may be obtained by adding a first parameter in the matcner
528 * // callback, which will receive the index in its most compact type (just
529 * // use `size_t` if the exact type is not important), e.g.:
530 * char* foo(Variant<A, B, C, D>& v) {
531 * return v.match([](auto aIndex, auto& aAlternative) {...});
532 * // --OR--
533 * return v.match([](size_t aIndex, auto& aAlternative) {...});
536 * ## Examples
538 * A tree is either an empty leaf, or a node with a value and two children:
540 * struct Leaf { };
542 * template<typename T>
543 * struct Node
545 * T value;
546 * Tree<T>* left;
547 * Tree<T>* right;
548 * };
550 * template<typename T>
551 * using Tree = Variant<Leaf, Node<T>>;
553 * A copy-on-write string is either a non-owning reference to some existing
554 * string, or an owning reference to our copy:
556 * class CopyOnWriteString
558 * Variant<const char*, UniquePtr<char[]>> string;
560 * ...
561 * };
563 * Because Variant must be aligned suitable to hold any value stored within it,
564 * and because |alignas| requirements don't affect platform ABI with respect to
565 * how parameters are laid out in memory, Variant can't be used as the type of a
566 * function parameter. Pass Variant to functions by pointer or reference
567 * instead.
569 template <typename... Ts>
570 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
571 friend struct IPC::ParamTraits<mozilla::Variant<Ts...>>;
572 friend struct mozilla::ipc::IPDLParamTraits<mozilla::Variant<Ts...>>;
574 using Tag = typename detail::VariantTag<Ts...>::Type;
575 using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
577 static constexpr size_t RawDataAlignment = tl::Max<alignof(Ts)...>::value;
578 static constexpr size_t RawDataSize = tl::Max<sizeof(Ts)...>::value;
580 // Raw storage for the contained variant value.
581 alignas(RawDataAlignment) unsigned char rawData[RawDataSize];
583 // Each type is given a unique tag value that lets us keep track of the
584 // contained variant value's type.
585 Tag tag;
587 // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
588 // -Werror compile error) to reinterpret_cast<> |rawData| to |T*|, even
589 // through |void*|. Placing the latter cast in these separate functions
590 // breaks the chain such that affected GCC versions no longer warn/error.
591 void* ptr() { return rawData; }
593 const void* ptr() const { return rawData; }
595 public:
596 /** Perfect forwarding construction for some variant type T. */
597 template <typename RefT,
598 // RefT captures both const& as well as && (as intended, to support
599 // perfect forwarding), so we have to remove those qualifiers here
600 // when ensuring that T is a variant of this type, and getting T's
601 // tag, etc.
602 typename T = typename detail::SelectVariantType<RefT, Ts...>::Type>
603 explicit Variant(RefT&& aT) : tag(Impl::template tag<T>()) {
604 static_assert(
605 detail::SelectVariantType<RefT, Ts...>::count == 1,
606 "Variant can only be selected by type if that type is unique");
607 ::new (KnownNotNull, ptr()) T(std::forward<RefT>(aT));
611 * Perfect forwarding construction for some variant type T, by
612 * explicitly giving the type.
613 * This is necessary to construct from any number of arguments,
614 * or to convert from a type that is not in the Variant's type list.
616 template <typename T, typename... Args>
617 MOZ_IMPLICIT Variant(const VariantType<T>&, Args&&... aTs)
618 : tag(Impl::template tag<T>()) {
619 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
623 * Perfect forwarding construction for some variant type T, by
624 * explicitly giving the type index.
625 * This is necessary to construct from any number of arguments,
626 * or to convert from a type that is not in the Variant's type list,
627 * or to construct a type that is present more than once in the Variant.
629 template <size_t N, typename... Args>
630 MOZ_IMPLICIT Variant(const VariantIndex<N>&, Args&&... aTs) : tag(N) {
631 using T = typename detail::Nth<N, Ts...>::Type;
632 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
636 * Constructs this Variant from an AsVariantTemporary<T> such that T can be
637 * stored in one of the types allowable in this Variant. This is used in the
638 * implementation of AsVariant().
640 template <typename RefT>
641 MOZ_IMPLICIT Variant(detail::AsVariantTemporary<RefT>&& aValue)
642 : tag(Impl::template tag<
643 typename detail::SelectVariantType<RefT, Ts...>::Type>()) {
644 using T = typename detail::SelectVariantType<RefT, Ts...>::Type;
645 static_assert(
646 detail::SelectVariantType<RefT, Ts...>::count == 1,
647 "Variant can only be selected by type if that type is unique");
648 ::new (KnownNotNull, ptr()) T(std::move(aValue.mValue));
651 /** Copy construction. */
652 Variant(const Variant& aRhs) : tag(aRhs.tag) {
653 Impl::copyConstruct(ptr(), aRhs);
656 /** Move construction. */
657 Variant(Variant&& aRhs) : tag(aRhs.tag) {
658 Impl::moveConstruct(ptr(), std::move(aRhs));
661 /** Copy assignment. */
662 Variant& operator=(const Variant& aRhs) {
663 MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
664 this->~Variant();
665 ::new (KnownNotNull, this) Variant(aRhs);
666 return *this;
669 /** Move assignment. */
670 Variant& operator=(Variant&& aRhs) {
671 MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
672 this->~Variant();
673 ::new (KnownNotNull, this) Variant(std::move(aRhs));
674 return *this;
677 /** Move assignment from AsVariant(). */
678 template <typename T>
679 Variant& operator=(detail::AsVariantTemporary<T>&& aValue) {
680 static_assert(
681 detail::SelectVariantType<T, Ts...>::count == 1,
682 "Variant can only be selected by type if that type is unique");
683 this->~Variant();
684 ::new (KnownNotNull, this) Variant(std::move(aValue));
685 return *this;
688 ~Variant() { Impl::destroy(*this); }
690 template <typename T, typename... Args>
691 T& emplace(Args&&... aTs) {
692 Impl::destroy(*this);
693 tag = Impl::template tag<T>();
694 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
695 return as<T>();
698 template <size_t N, typename... Args>
699 typename detail::Nth<N, Ts...>::Type& emplace(Args&&... aTs) {
700 using T = typename detail::Nth<N, Ts...>::Type;
701 Impl::destroy(*this);
702 tag = N;
703 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
704 return as<N>();
707 /** Check which variant type is currently contained. */
708 template <typename T>
709 bool is() const {
710 static_assert(
711 detail::SelectVariantType<T, Ts...>::count == 1,
712 "provided a type not uniquely found in this Variant's type list");
713 return Impl::template tag<T>() == tag;
716 template <size_t N>
717 bool is() const {
718 static_assert(N < sizeof...(Ts),
719 "provided an index outside of this Variant's type list");
720 return N == size_t(tag);
724 * Operator == overload that defers to the variant type's operator==
725 * implementation if the rhs is tagged as the same type as this one.
727 bool operator==(const Variant& aRhs) const {
728 return tag == aRhs.tag && Impl::equal(*this, aRhs);
732 * Operator != overload that defers to the negation of the variant type's
733 * operator== implementation if the rhs is tagged as the same type as this
734 * one.
736 bool operator!=(const Variant& aRhs) const { return !(*this == aRhs); }
738 // Accessors for working with the contained variant value.
740 /** Mutable lvalue-reference. */
741 template <typename T>
742 T& as() & {
743 static_assert(
744 detail::SelectVariantType<T, Ts...>::count == 1,
745 "provided a type not uniquely found in this Variant's type list");
746 MOZ_RELEASE_ASSERT(is<T>());
747 return *static_cast<T*>(ptr());
750 template <size_t N>
751 typename detail::Nth<N, Ts...>::Type& as() & {
752 static_assert(N < sizeof...(Ts),
753 "provided an index outside of this Variant's type list");
754 MOZ_RELEASE_ASSERT(is<N>());
755 return *static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr());
758 /** Immutable const lvalue-reference. */
759 template <typename T>
760 const T& as() const& {
761 static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
762 "provided a type not found in this Variant's type list");
763 MOZ_RELEASE_ASSERT(is<T>());
764 return *static_cast<const T*>(ptr());
767 template <size_t N>
768 const typename detail::Nth<N, Ts...>::Type& as() const& {
769 static_assert(N < sizeof...(Ts),
770 "provided an index outside of this Variant's type list");
771 MOZ_RELEASE_ASSERT(is<N>());
772 return *static_cast<const typename detail::Nth<N, Ts...>::Type*>(ptr());
775 /** Mutable rvalue-reference. */
776 template <typename T>
777 T&& as() && {
778 static_assert(
779 detail::SelectVariantType<T, Ts...>::count == 1,
780 "provided a type not uniquely found in this Variant's type list");
781 MOZ_RELEASE_ASSERT(is<T>());
782 return std::move(*static_cast<T*>(ptr()));
785 template <size_t N>
786 typename detail::Nth<N, Ts...>::Type&& as() && {
787 static_assert(N < sizeof...(Ts),
788 "provided an index outside of this Variant's type list");
789 MOZ_RELEASE_ASSERT(is<N>());
790 return std::move(
791 *static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr()));
794 /** Immutable const rvalue-reference. */
795 template <typename T>
796 const T&& as() const&& {
797 static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
798 "provided a type not found in this Variant's type list");
799 MOZ_RELEASE_ASSERT(is<T>());
800 return std::move(*static_cast<const T*>(ptr()));
803 template <size_t N>
804 const typename detail::Nth<N, Ts...>::Type&& as() const&& {
805 static_assert(N < sizeof...(Ts),
806 "provided an index outside of this Variant's type list");
807 MOZ_RELEASE_ASSERT(is<N>());
808 return std::move(
809 *static_cast<const typename detail::Nth<N, Ts...>::Type*>(ptr()));
813 * Extract the contained variant value from this container into a temporary
814 * value. On completion, the value in the variant will be in a
815 * safely-destructible state, as determined by the behavior of T's move
816 * constructor when provided the variant's internal value.
818 template <typename T>
819 T extract() {
820 static_assert(
821 detail::SelectVariantType<T, Ts...>::count == 1,
822 "provided a type not uniquely found in this Variant's type list");
823 MOZ_ASSERT(is<T>());
824 return T(std::move(as<T>()));
827 template <size_t N>
828 typename detail::Nth<N, Ts...>::Type extract() {
829 static_assert(N < sizeof...(Ts),
830 "provided an index outside of this Variant's type list");
831 MOZ_RELEASE_ASSERT(is<N>());
832 return typename detail::Nth<N, Ts...>::Type(std::move(as<N>()));
835 // Exhaustive matching of all variant types on the contained value.
837 /** Match on an immutable const lvalue-reference. */
838 template <typename Matcher>
839 decltype(auto) match(Matcher&& aMatcher) const& {
840 return Impl::match(std::forward<Matcher>(aMatcher), *this);
843 template <typename M0, typename M1, typename... Ms>
844 decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const& {
845 return matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
846 std::forward<Ms>(aMs)...);
849 /** Match on a mutable non-const lvalue-reference. */
850 template <typename Matcher>
851 decltype(auto) match(Matcher&& aMatcher) & {
852 return Impl::match(std::forward<Matcher>(aMatcher), *this);
855 template <typename M0, typename M1, typename... Ms>
856 decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) & {
857 return matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
858 std::forward<Ms>(aMs)...);
861 /** Match on an immutable const rvalue-reference. */
862 template <typename Matcher>
863 decltype(auto) match(Matcher&& aMatcher) const&& {
864 return Impl::match(std::forward<Matcher>(aMatcher), std::move(*this));
867 template <typename M0, typename M1, typename... Ms>
868 decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const&& {
869 return matchN(std::move(*this), std::forward<M0>(aM0),
870 std::forward<M1>(aM1), std::forward<Ms>(aMs)...);
873 /** Match on a mutable non-const rvalue-reference. */
874 template <typename Matcher>
875 decltype(auto) match(Matcher&& aMatcher) && {
876 return Impl::match(std::forward<Matcher>(aMatcher), std::move(*this));
879 template <typename M0, typename M1, typename... Ms>
880 decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) && {
881 return matchN(std::move(*this), std::forward<M0>(aM0),
882 std::forward<M1>(aM1), std::forward<Ms>(aMs)...);
886 * Incorporate the current variant's tag into hashValue.
887 * Note that this does not hash the actual contents; you must take
888 * care of that yourself, perhaps by using a match.
890 mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) const {
891 return mozilla::AddToHash(hashValue, tag);
894 private:
895 template <typename ConcreteVariant, typename M0, typename M1, typename... Ms>
896 static decltype(auto) matchN(ConcreteVariant&& aVariant, M0&& aM0, M1&& aM1,
897 Ms&&... aMs) {
898 static_assert(
899 2 + sizeof...(Ms) == sizeof...(Ts),
900 "Variant<T...>::match() takes either one callable argument that "
901 "accepts every type T; or one for each type T, in order");
902 return Impl::matchN(std::forward<ConcreteVariant>(aVariant),
903 std::forward<M0>(aM0), std::forward<M1>(aM1),
904 std::forward<Ms>(aMs)...);
909 * AsVariant() is used to construct a Variant<T,...> value containing the
910 * provided T value using type inference. It can be used to construct Variant
911 * values in expressions or return them from functions without specifying the
912 * entire Variant type.
914 * Because AsVariant() must copy or move the value into a temporary and this
915 * cannot necessarily be elided by the compiler, it's mostly appropriate only
916 * for use with primitive or very small types.
918 * AsVariant() returns a AsVariantTemporary value which is implicitly
919 * convertible to any Variant that can hold a value of type T.
921 template <typename T>
922 detail::AsVariantTemporary<T> AsVariant(T&& aValue) {
923 return detail::AsVariantTemporary<T>(std::forward<T>(aValue));
926 } // namespace mozilla
928 #endif /* mozilla_Variant_h */