Source code

Revision control

Other Tools

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/. */
6
7
/* A template class for tagged unions. */
8
9
#include <new>
10
#include <stdint.h>
11
12
#include "mozilla/Assertions.h"
13
#include "mozilla/FunctionTypeTraits.h"
14
#include "mozilla/HashFunctions.h"
15
#include "mozilla/OperatorNewExtensions.h"
16
#include "mozilla/TemplateLib.h"
17
#include "mozilla/TypeTraits.h"
18
#include <utility>
19
20
#ifndef mozilla_Variant_h
21
# define mozilla_Variant_h
22
23
namespace IPC {
24
template <typename T>
25
struct ParamTraits;
26
} // namespace IPC
27
28
namespace mozilla {
29
30
namespace ipc {
31
template <typename T>
32
struct IPDLParamTraits;
33
} // namespace ipc
34
35
template <typename... Ts>
36
class Variant;
37
38
namespace detail {
39
40
// Nth<N, types...>::Type is the Nth type (0-based) in the list of types Ts.
41
template <size_t N, typename... Ts>
42
struct Nth;
43
44
template <typename T, typename... Ts>
45
struct Nth<0, T, Ts...> {
46
using Type = T;
47
};
48
49
template <size_t N, typename T, typename... Ts>
50
struct Nth<N, T, Ts...> {
51
using Type = typename Nth<N - 1, Ts...>::Type;
52
};
53
54
/// SelectVariantTypeHelper is used in the implementation of SelectVariantType.
55
template <typename T, typename... Variants>
56
struct SelectVariantTypeHelper;
57
58
template <typename T>
59
struct SelectVariantTypeHelper<T> {
60
static constexpr size_t count = 0;
61
};
62
63
template <typename T, typename... Variants>
64
struct SelectVariantTypeHelper<T, T, Variants...> {
65
typedef T Type;
66
static constexpr size_t count =
67
1 + SelectVariantTypeHelper<T, Variants...>::count;
68
};
69
70
template <typename T, typename... Variants>
71
struct SelectVariantTypeHelper<T, const T, Variants...> {
72
typedef const T Type;
73
static constexpr size_t count =
74
1 + SelectVariantTypeHelper<T, Variants...>::count;
75
};
76
77
template <typename T, typename... Variants>
78
struct SelectVariantTypeHelper<T, const T&, Variants...> {
79
typedef const T& Type;
80
static constexpr size_t count =
81
1 + SelectVariantTypeHelper<T, Variants...>::count;
82
};
83
84
template <typename T, typename... Variants>
85
struct SelectVariantTypeHelper<T, T&&, Variants...> {
86
typedef T&& Type;
87
static constexpr size_t count =
88
1 + SelectVariantTypeHelper<T, Variants...>::count;
89
};
90
91
template <typename T, typename Head, typename... Variants>
92
struct SelectVariantTypeHelper<T, Head, Variants...>
93
: public SelectVariantTypeHelper<T, Variants...> {};
94
95
/**
96
* SelectVariantType takes a type T and a list of variant types Variants and
97
* yields a type Type, selected from Variants, that can store a value of type T
98
* or a reference to type T. If no such type was found, Type is not defined.
99
* SelectVariantType also has a `count` member that contains the total number of
100
* selectable types (which will be used to check that a requested type is not
101
* ambiguously present twice.)
102
*/
103
template <typename T, typename... Variants>
104
struct SelectVariantType
105
: public SelectVariantTypeHelper<
106
typename RemoveConst<typename RemoveReference<T>::Type>::Type,
107
Variants...> {};
108
109
// Compute a fast, compact type that can be used to hold integral values that
110
// distinctly map to every type in Ts.
111
template <typename... Ts>
112
struct VariantTag {
113
private:
114
static const size_t TypeCount = sizeof...(Ts);
115
116
public:
117
using Type = typename Conditional < TypeCount < 3, bool,
118
typename Conditional<TypeCount<(1 << 8), uint_fast8_t,
119
size_t // stop caring past a certain
120
// point :-)
121
>::Type>::Type;
122
};
123
124
// TagHelper gets the given sentinel tag value for the given type T. This has to
125
// be split out from VariantImplementation because you can't nest a partial
126
// template specialization within a template class.
127
128
template <typename Tag, size_t N, typename T, typename U, typename Next,
129
bool isMatch>
130
struct TagHelper;
131
132
// In the case where T != U, we continue recursion.
133
template <typename Tag, size_t N, typename T, typename U, typename Next>
134
struct TagHelper<Tag, N, T, U, Next, false> {
135
static Tag tag() { return Next::template tag<U>(); }
136
};
137
138
// In the case where T == U, return the tag number.
139
template <typename Tag, size_t N, typename T, typename U, typename Next>
140
struct TagHelper<Tag, N, T, U, Next, true> {
141
static Tag tag() { return Tag(N); }
142
};
143
144
// The VariantImplementation template provides the guts of mozilla::Variant. We
145
// create a VariantImplementation for each T in Ts... which handles
146
// construction, destruction, etc for when the Variant's type is T. If the
147
// Variant's type isn't T, it punts the request on to the next
148
// VariantImplementation.
149
150
template <typename Tag, size_t N, typename... Ts>
151
struct VariantImplementation;
152
153
// The singly typed Variant / recursion base case.
154
template <typename Tag, size_t N, typename T>
155
struct VariantImplementation<Tag, N, T> {
156
template <typename U>
157
static Tag tag() {
158
static_assert(mozilla::IsSame<T, U>::value,
159
"mozilla::Variant: tag: bad type!");
160
return Tag(N);
161
}
162
163
template <typename Variant>
164
static void copyConstruct(void* aLhs, const Variant& aRhs) {
165
::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
166
}
167
168
template <typename Variant>
169
static void moveConstruct(void* aLhs, Variant&& aRhs) {
170
::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
171
}
172
173
template <typename Variant>
174
static void destroy(Variant& aV) {
175
aV.template as<N>().~T();
176
}
177
178
template <typename Variant>
179
static bool equal(const Variant& aLhs, const Variant& aRhs) {
180
return aLhs.template as<N>() == aRhs.template as<N>();
181
}
182
183
template <typename Matcher, typename ConcreteVariant>
184
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
185
return aMatcher(aV.template as<N>());
186
}
187
188
template <typename ConcreteVariant, typename Matcher>
189
static decltype(auto) matchN(ConcreteVariant& aV, Matcher&& aMatcher) {
190
return aMatcher(aV.template as<N>());
191
}
192
};
193
194
// VariantImplementation for some variant type T.
195
template <typename Tag, size_t N, typename T, typename... Ts>
196
struct VariantImplementation<Tag, N, T, Ts...> {
197
// The next recursive VariantImplementation.
198
using Next = VariantImplementation<Tag, N + 1, Ts...>;
199
200
template <typename U>
201
static Tag tag() {
202
return TagHelper<Tag, N, T, U, Next, IsSame<T, U>::value>::tag();
203
}
204
205
template <typename Variant>
206
static void copyConstruct(void* aLhs, const Variant& aRhs) {
207
if (aRhs.template is<N>()) {
208
::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
209
} else {
210
Next::copyConstruct(aLhs, aRhs);
211
}
212
}
213
214
template <typename Variant>
215
static void moveConstruct(void* aLhs, Variant&& aRhs) {
216
if (aRhs.template is<N>()) {
217
::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
218
} else {
219
Next::moveConstruct(aLhs, std::move(aRhs));
220
}
221
}
222
223
template <typename Variant>
224
static void destroy(Variant& aV) {
225
if (aV.template is<N>()) {
226
aV.template as<N>().~T();
227
} else {
228
Next::destroy(aV);
229
}
230
}
231
232
template <typename Variant>
233
static bool equal(const Variant& aLhs, const Variant& aRhs) {
234
if (aLhs.template is<N>()) {
235
MOZ_ASSERT(aRhs.template is<N>());
236
return aLhs.template as<N>() == aRhs.template as<N>();
237
} else {
238
return Next::equal(aLhs, aRhs);
239
}
240
}
241
242
template <typename Matcher, typename ConcreteVariant>
243
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
244
if (aV.template is<N>()) {
245
return aMatcher(aV.template as<N>());
246
} else {
247
// If you're seeing compilation errors here like "no matching
248
// function for call to 'match'" then that means that the
249
// Matcher doesn't exhaust all variant types. There must exist a
250
// Matcher::operator()(T&) for every variant type T.
251
//
252
// If you're seeing compilation errors here like "cannot initialize
253
// return object of type <...> with an rvalue of type <...>" then that
254
// means that the Matcher::operator()(T&) overloads are returning
255
// different types. They must all return the same type.
256
return Next::match(std::forward<Matcher>(aMatcher), aV);
257
}
258
}
259
260
template <typename ConcreteVariant, typename Mi, typename... Ms>
261
static decltype(auto) matchN(ConcreteVariant& aV, Mi&& aMi, Ms&&... aMs) {
262
if (aV.template is<N>()) {
263
return aMi(aV.template as<N>());
264
} else {
265
// If you're seeing compilation errors here like "no matching
266
// function for call to 'match'" then that means that the
267
// Matchers don't exhaust all variant types. There must exist a
268
// Matcher (with its operator()(T&)) for every variant type T, in the
269
// exact same order.
270
return Next::matchN(aV, std::forward<Ms>(aMs)...);
271
}
272
}
273
};
274
275
/**
276
* AsVariantTemporary stores a value of type T to allow construction of a
277
* Variant value via type inference. Because T is copied and there's no
278
* guarantee that the copy can be elided, AsVariantTemporary is best used with
279
* primitive or very small types.
280
*/
281
template <typename T>
282
struct AsVariantTemporary {
283
explicit AsVariantTemporary(const T& aValue) : mValue(aValue) {}
284
285
template <typename U>
286
explicit AsVariantTemporary(U&& aValue) : mValue(std::forward<U>(aValue)) {}
287
288
AsVariantTemporary(const AsVariantTemporary& aOther)
289
: mValue(aOther.mValue) {}
290
291
AsVariantTemporary(AsVariantTemporary&& aOther)
292
: mValue(std::move(aOther.mValue)) {}
293
294
AsVariantTemporary() = delete;
295
void operator=(const AsVariantTemporary&) = delete;
296
void operator=(AsVariantTemporary&&) = delete;
297
298
typename RemoveConst<typename RemoveReference<T>::Type>::Type mValue;
299
};
300
301
} // namespace detail
302
303
// Used to unambiguously specify one of the Variant's type.
304
template <typename T>
305
struct VariantType {
306
using Type = T;
307
};
308
309
// Used to specify one of the Variant's type by index.
310
template <size_t N>
311
struct VariantIndex {
312
static constexpr size_t index = N;
313
};
314
315
/**
316
* # mozilla::Variant
317
*
318
* A variant / tagged union / heterogenous disjoint union / sum-type template
319
* class. Similar in concept to (but not derived from) `boost::variant`.
320
*
321
* Sometimes, you may wish to use a C union with non-POD types. However, this is
322
* forbidden in C++ because it is not clear which type in the union should have
323
* its constructor and destructor run on creation and deletion
324
* respectively. This is the problem that `mozilla::Variant` solves.
325
*
326
* ## Usage
327
*
328
* A `mozilla::Variant` instance is constructed (via move or copy) from one of
329
* its variant types (ignoring const and references). It does *not* support
330
* construction from subclasses of variant types or types that coerce to one of
331
* the variant types.
332
*
333
* Variant<char, uint32_t> v1('a');
334
* Variant<UniquePtr<A>, B, C> v2(MakeUnique<A>());
335
* Variant<bool, char> v3(VariantType<char>, 0); // disambiguation needed
336
* Variant<int, int> v4(VariantIndex<1>, 0); // 2nd int
337
*
338
* Because specifying the full type of a Variant value is often verbose,
339
* there are two easier ways to construct values:
340
*
341
* A. AsVariant() can be used to construct a Variant value using type inference
342
* in contexts such as expressions or when returning values from functions.
343
* Because AsVariant() must copy or move the value into a temporary and this
344
* cannot necessarily be elided by the compiler, it's mostly appropriate only
345
* for use with primitive or very small types.
346
*
347
* Variant<char, uint32_t> Foo() { return AsVariant('x'); }
348
* // ...
349
* Variant<char, uint32_t> v1 = Foo(); // v1 holds char('x').
350
*
351
* B. Brace-construction with VariantType or VariantIndex; this also allows
352
* in-place construction with any number of arguments.
353
*
354
* struct AB { AB(int, int){...} };
355
* static Variant<AB, bool> foo()
356
* {
357
* return {VariantIndex<0>{}, 1, 2};
358
* }
359
* // ...
360
* Variant<AB, bool> v0 = Foo(); // v0 holds AB(1,2).
361
*
362
* All access to the contained value goes through type-safe accessors.
363
* Either the stored type, or the type index may be provided.
364
*
365
* void
366
* Foo(Variant<A, B, C> v)
367
* {
368
* if (v.is<A>()) {
369
* A& ref = v.as<A>();
370
* ...
371
* } else (v.is<1>()) { // Instead of v.is<B>.
372
* ...
373
* } else {
374
* ...
375
* }
376
* }
377
*
378
* In some situation, a Variant may be constructed from templated types, in
379
* which case it is possible that the same type could be given multiple times by
380
* an external developer. Or seemingly-different types could be aliases.
381
* In this case, repeated types can only be accessed through their index, to
382
* prevent ambiguous access by type.
383
*
384
* // Bad!
385
* template <typename T>
386
* struct ResultOrError
387
* {
388
* Variant<T, int> m;
389
* ResultOrError() : m(int(0)) {} // Error '0' by default
390
* ResultOrError(const T& r) : m(r) {}
391
* bool IsResult() const { return m.is<T>(); }
392
* bool IsError() const { return m.is<int>(); }
393
* };
394
* // Now instantiante with the result being an int too:
395
* ResultOrError<int> myResult(123); // Fail!
396
* // In Variant<int, int>, which 'int' are we refering to, from inside
397
* // ResultOrError functions?
398
*
399
* // Good!
400
* template <typename T>
401
* struct ResultOrError
402
* {
403
* Variant<T, int> m;
404
* ResultOrError() : m(VariantIndex<1>{}, 0) {} // Error '0' by default
405
* ResultOrError(const T& r) : m(VariantIndex<0>{}, r) {}
406
* bool IsResult() const { return m.is<0>(); } // 0 -> T
407
* bool IsError() const { return m.is<1>(); } // 1 -> int
408
* };
409
* // Now instantiante with the result being an int too:
410
* ResultOrError<int> myResult(123); // It now works!
411
*
412
* Attempting to use the contained value as type `T1` when the `Variant`
413
* instance contains a value of type `T2` causes an assertion failure.
414
*
415
* A a;
416
* Variant<A, B, C> v(a);
417
* v.as<B>(); // <--- Assertion failure!
418
*
419
* Trying to use a `Variant<Ts...>` instance as some type `U` that is not a
420
* member of the set of `Ts...` is a compiler error.
421
*
422
* A a;
423
* Variant<A, B, C> v(a);
424
* v.as<SomeRandomType>(); // <--- Compiler error!
425
*
426
* Additionally, you can turn a `Variant` that `is<T>` into a `T` by moving it
427
* out of the containing `Variant` instance with the `extract<T>` method:
428
*
429
* Variant<UniquePtr<A>, B, C> v(MakeUnique<A>());
430
* auto ptr = v.extract<UniquePtr<A>>();
431
*
432
* Finally, you can exhaustively match on the contained variant and branch into
433
* different code paths depending on which type is contained. This is preferred
434
* to manually checking every variant type T with is<T>() because it provides
435
* compile-time checking that you handled every type, rather than runtime
436
* assertion failures.
437
*
438
* // Bad!
439
* char* foo(Variant<A, B, C, D>& v) {
440
* if (v.is<A>()) {
441
* return ...;
442
* } else if (v.is<B>()) {
443
* return ...;
444
* } else {
445
* return doSomething(v.as<C>()); // Forgot about case D!
446
* }
447
* }
448
*
449
* // Instead, a single function object (that can deal with all possible
450
* // options) may be provided:
451
* struct FooMatcher
452
* {
453
* // The return type of all matchers must be identical.
454
* char* operator()(A& a) { ... }
455
* char* operator()(B& b) { ... }
456
* char* operator()(C& c) { ... }
457
* char* operator()(D& d) { ... } // Compile-time error to forget D!
458
* }
459
* char* foo(Variant<A, B, C, D>& v) {
460
* return v.match(FooMatcher());
461
* }
462
*
463
* // In some situations, a single generic lambda may also be appropriate:
464
* char* foo(Variant<A, B, C, D>& v) {
465
* return v.match([](auto&){...});
466
* }
467
*
468
* // Alternatively, multiple function objects may be provided, each one
469
* // corresponding to an option, in the same order:
470
* char* foo(Variant<A, B, C, D>& v) {
471
* return v.match([](A&) { ... },
472
* [](B&) { ... },
473
* [](C&) { ... },
474
* [](D&) { ... });
475
* }
476
*
477
* ## Examples
478
*
479
* A tree is either an empty leaf, or a node with a value and two children:
480
*
481
* struct Leaf { };
482
*
483
* template<typename T>
484
* struct Node
485
* {
486
* T value;
487
* Tree<T>* left;
488
* Tree<T>* right;
489
* };
490
*
491
* template<typename T>
492
* using Tree = Variant<Leaf, Node<T>>;
493
*
494
* A copy-on-write string is either a non-owning reference to some existing
495
* string, or an owning reference to our copy:
496
*
497
* class CopyOnWriteString
498
* {
499
* Variant<const char*, UniquePtr<char[]>> string;
500
*
501
* ...
502
* };
503
*
504
* Because Variant must be aligned suitable to hold any value stored within it,
505
* and because |alignas| requirements don't affect platform ABI with respect to
506
* how parameters are laid out in memory, Variant can't be used as the type of a
507
* function parameter. Pass Variant to functions by pointer or reference
508
* instead.
509
*/
510
template <typename... Ts>
511
class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
512
friend struct IPC::ParamTraits<mozilla::Variant<Ts...>>;
513
friend struct mozilla::ipc::IPDLParamTraits<mozilla::Variant<Ts...>>;
514
515
using Tag = typename detail::VariantTag<Ts...>::Type;
516
using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
517
518
static constexpr size_t RawDataAlignment = tl::Max<alignof(Ts)...>::value;
519
static constexpr size_t RawDataSize = tl::Max<sizeof(Ts)...>::value;
520
521
// Raw storage for the contained variant value.
522
alignas(RawDataAlignment) unsigned char rawData[RawDataSize];
523
524
// Each type is given a unique tag value that lets us keep track of the
525
// contained variant value's type.
526
Tag tag;
527
528
// Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
529
// -Werror compile error) to reinterpret_cast<> |rawData| to |T*|, even
530
// through |void*|. Placing the latter cast in these separate functions
531
// breaks the chain such that affected GCC versions no longer warn/error.
532
void* ptr() { return rawData; }
533
534
const void* ptr() const { return rawData; }
535
536
public:
537
/** Perfect forwarding construction for some variant type T. */
538
template <typename RefT,
539
// RefT captures both const& as well as && (as intended, to support
540
// perfect forwarding), so we have to remove those qualifiers here
541
// when ensuring that T is a variant of this type, and getting T's
542
// tag, etc.
543
typename T = typename detail::SelectVariantType<RefT, Ts...>::Type>
544
explicit Variant(RefT&& aT) : tag(Impl::template tag<T>()) {
545
static_assert(
546
detail::SelectVariantType<RefT, Ts...>::count == 1,
547
"Variant can only be selected by type if that type is unique");
548
::new (KnownNotNull, ptr()) T(std::forward<RefT>(aT));
549
}
550
551
/**
552
* Perfect forwarding construction for some variant type T, by
553
* explicitly giving the type.
554
* This is necessary to construct from any number of arguments,
555
* or to convert from a type that is not in the Variant's type list.
556
*/
557
template <typename T, typename... Args>
558
MOZ_IMPLICIT Variant(const VariantType<T>&, Args&&... aTs)
559
: tag(Impl::template tag<T>()) {
560
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
561
}
562
563
/**
564
* Perfect forwarding construction for some variant type T, by
565
* explicitly giving the type index.
566
* This is necessary to construct from any number of arguments,
567
* or to convert from a type that is not in the Variant's type list,
568
* or to construct a type that is present more than once in the Variant.
569
*/
570
template <size_t N, typename... Args>
571
MOZ_IMPLICIT Variant(const VariantIndex<N>&, Args&&... aTs) : tag(N) {
572
using T = typename detail::Nth<N, Ts...>::Type;
573
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
574
}
575
576
/**
577
* Constructs this Variant from an AsVariantTemporary<T> such that T can be
578
* stored in one of the types allowable in this Variant. This is used in the
579
* implementation of AsVariant().
580
*/
581
template <typename RefT>
582
MOZ_IMPLICIT Variant(detail::AsVariantTemporary<RefT>&& aValue)
583
: tag(Impl::template tag<
584
typename detail::SelectVariantType<RefT, Ts...>::Type>()) {
585
using T = typename detail::SelectVariantType<RefT, Ts...>::Type;
586
static_assert(
587
detail::SelectVariantType<RefT, Ts...>::count == 1,
588
"Variant can only be selected by type if that type is unique");
589
::new (KnownNotNull, ptr()) T(std::move(aValue.mValue));
590
}
591
592
/** Copy construction. */
593
Variant(const Variant& aRhs) : tag(aRhs.tag) {
594
Impl::copyConstruct(ptr(), aRhs);
595
}
596
597
/** Move construction. */
598
Variant(Variant&& aRhs) : tag(aRhs.tag) {
599
Impl::moveConstruct(ptr(), std::move(aRhs));
600
}
601
602
/** Copy assignment. */
603
Variant& operator=(const Variant& aRhs) {
604
MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
605
this->~Variant();
606
::new (KnownNotNull, this) Variant(aRhs);
607
return *this;
608
}
609
610
/** Move assignment. */
611
Variant& operator=(Variant&& aRhs) {
612
MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
613
this->~Variant();
614
::new (KnownNotNull, this) Variant(std::move(aRhs));
615
return *this;
616
}
617
618
/** Move assignment from AsVariant(). */
619
template <typename T>
620
Variant& operator=(detail::AsVariantTemporary<T>&& aValue) {
621
static_assert(
622
detail::SelectVariantType<T, Ts...>::count == 1,
623
"Variant can only be selected by type if that type is unique");
624
this->~Variant();
625
::new (KnownNotNull, this) Variant(std::move(aValue));
626
return *this;
627
}
628
629
~Variant() { Impl::destroy(*this); }
630
631
template <typename T, typename... Args>
632
T& emplace(Args&&... aTs) {
633
Impl::destroy(*this);
634
tag = Impl::template tag<T>();
635
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
636
return as<T>();
637
}
638
639
template <size_t N, typename... Args>
640
typename detail::Nth<N, Ts...>::Type& emplace(Args&&... aTs) {
641
using T = typename detail::Nth<N, Ts...>::Type;
642
Impl::destroy(*this);
643
tag = N;
644
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
645
return as<N>();
646
}
647
648
/** Check which variant type is currently contained. */
649
template <typename T>
650
bool is() const {
651
static_assert(
652
detail::SelectVariantType<T, Ts...>::count == 1,
653
"provided a type not uniquely found in this Variant's type list");
654
return Impl::template tag<T>() == tag;
655
}
656
657
template <size_t N>
658
bool is() const {
659
static_assert(N < sizeof...(Ts),
660
"provided an index outside of this Variant's type list");
661
return N == size_t(tag);
662
}
663
664
/**
665
* Operator == overload that defers to the variant type's operator==
666
* implementation if the rhs is tagged as the same type as this one.
667
*/
668
bool operator==(const Variant& aRhs) const {
669
return tag == aRhs.tag && Impl::equal(*this, aRhs);
670
}
671
672
/**
673
* Operator != overload that defers to the negation of the variant type's
674
* operator== implementation if the rhs is tagged as the same type as this
675
* one.
676
*/
677
bool operator!=(const Variant& aRhs) const { return !(*this == aRhs); }
678
679
// Accessors for working with the contained variant value.
680
681
/** Mutable reference. */
682
template <typename T>
683
T& as() {
684
static_assert(
685
detail::SelectVariantType<T, Ts...>::count == 1,
686
"provided a type not uniquely found in this Variant's type list");
687
MOZ_RELEASE_ASSERT(is<T>());
688
return *static_cast<T*>(ptr());
689
}
690
691
template <size_t N>
692
typename detail::Nth<N, Ts...>::Type& as() {
693
static_assert(N < sizeof...(Ts),
694
"provided an index outside of this Variant's type list");
695
MOZ_RELEASE_ASSERT(is<N>());
696
return *static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr());
697
}
698
699
/** Immutable const reference. */
700
template <typename T>
701
const T& as() const {
702
static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
703
"provided a type not found in this Variant's type list");
704
MOZ_RELEASE_ASSERT(is<T>());
705
return *static_cast<const T*>(ptr());
706
}
707
708
template <size_t N>
709
const typename detail::Nth<N, Ts...>::Type& as() const {
710
static_assert(N < sizeof...(Ts),
711
"provided an index outside of this Variant's type list");
712
MOZ_RELEASE_ASSERT(is<N>());
713
return *static_cast<const typename detail::Nth<N, Ts...>::Type*>(ptr());
714
}
715
716
/**
717
* Extract the contained variant value from this container into a temporary
718
* value. On completion, the value in the variant will be in a
719
* safely-destructible state, as determined by the behavior of T's move
720
* constructor when provided the variant's internal value.
721
*/
722
template <typename T>
723
T extract() {
724
static_assert(
725
detail::SelectVariantType<T, Ts...>::count == 1,
726
"provided a type not uniquely found in this Variant's type list");
727
MOZ_ASSERT(is<T>());
728
return T(std::move(as<T>()));
729
}
730
731
template <size_t N>
732
typename detail::Nth<N, Ts...>::Type extract() {
733
static_assert(N < sizeof...(Ts),
734
"provided an index outside of this Variant's type list");
735
MOZ_RELEASE_ASSERT(is<N>());
736
return typename detail::Nth<N, Ts...>::Type(std::move(as<N>()));
737
}
738
739
// Exhaustive matching of all variant types on the contained value.
740
741
/** Match on an immutable const reference. */
742
template <typename Matcher>
743
decltype(auto) match(Matcher&& aMatcher) const {
744
return Impl::match(std::forward<Matcher>(aMatcher), *this);
745
}
746
747
template <typename M0, typename M1, typename... Ms>
748
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const {
749
static_assert(
750
2 + sizeof...(Ms) == sizeof...(Ts),
751
"Variant<T...>::match() takes either one callable argument that "
752
"accepts every type T; or one for each type T, in order");
753
static_assert(
754
tl::And<IsSame<typename FunctionTypeTraits<M0>::ReturnType,
755
typename FunctionTypeTraits<M1>::ReturnType>::value,
756
IsSame<typename FunctionTypeTraits<M1>::ReturnType,
757
typename FunctionTypeTraits<Ms>::ReturnType>::value...>::
758
value,
759
"all matchers must have the same return type");
760
return Impl::matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
761
std::forward<Ms>(aMs)...);
762
}
763
764
/** Match on a mutable non-const reference. */
765
template <typename Matcher>
766
decltype(auto) match(Matcher&& aMatcher) {
767
return Impl::match(std::forward<Matcher>(aMatcher), *this);
768
}
769
770
template <typename M0, typename M1, typename... Ms>
771
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) {
772
static_assert(
773
2 + sizeof...(Ms) == sizeof...(Ts),
774
"Variant<T...>::match() takes either one callable argument that "
775
"accepts every type T; or one for each type T, in order");
776
static_assert(
777
tl::And<IsSame<typename FunctionTypeTraits<M0>::ReturnType,
778
typename FunctionTypeTraits<M1>::ReturnType>::value,
779
IsSame<typename FunctionTypeTraits<M0>::ReturnType,
780
typename FunctionTypeTraits<Ms>::ReturnType>::value...>::
781
value,
782
"all matchers must have the same return type");
783
return Impl::matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
784
std::forward<Ms>(aMs)...);
785
}
786
787
/**
788
* Incorporate the current variant's tag into hashValue.
789
* Note that this does not hash the actual contents; you must take
790
* care of that yourself, perhaps by using a match.
791
*/
792
mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) const {
793
return mozilla::AddToHash(hashValue, tag);
794
}
795
};
796
797
/*
798
* AsVariant() is used to construct a Variant<T,...> value containing the
799
* provided T value using type inference. It can be used to construct Variant
800
* values in expressions or return them from functions without specifying the
801
* entire Variant type.
802
*
803
* Because AsVariant() must copy or move the value into a temporary and this
804
* cannot necessarily be elided by the compiler, it's mostly appropriate only
805
* for use with primitive or very small types.
806
*
807
* AsVariant() returns a AsVariantTemporary value which is implicitly
808
* convertible to any Variant that can hold a value of type T.
809
*/
810
template <typename T>
811
detail::AsVariantTemporary<T> AsVariant(T&& aValue) {
812
return detail::AsVariantTemporary<T>(std::forward<T>(aValue));
813
}
814
815
} // namespace mozilla
816
817
#endif /* mozilla_Variant_h */