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/. */
8 * A class storing one of two optional value types that supports in-place lazy
12 #ifndef mozilla_MaybeOneOf_h
13 #define mozilla_MaybeOneOf_h
15 #include <stddef.h> // for size_t
17 #include <new> // for placement new
20 #include "mozilla/Assertions.h"
21 #include "mozilla/OperatorNewExtensions.h"
22 #include "mozilla/TemplateLib.h"
27 * MaybeOneOf<T1, T2> is like Maybe, but it supports constructing either T1
28 * or T2. When a MaybeOneOf<T1, T2> is constructed, it is |empty()|, i.e.,
29 * no value has been constructed and no destructor will be called when the
30 * MaybeOneOf<T1, T2> is destroyed. Upon calling |construct<T1>()| or
31 * |construct<T2>()|, a T1 or T2 object will be constructed with the given
32 * arguments and that object will be destroyed when the owning MaybeOneOf is
35 * Because MaybeOneOf must be aligned suitable to hold any value stored within
36 * it, and because |alignas| requirements don't affect platform ABI with respect
37 * to how parameters are laid out in memory, MaybeOneOf can't be used as the
38 * type of a function parameter. Pass MaybeOneOf to functions by pointer or
41 template <class T1
, class T2
>
42 class MOZ_NON_PARAM MaybeOneOf
{
43 static constexpr size_t StorageAlignment
=
44 tl::Max
<alignof(T1
), alignof(T2
)>::value
;
45 static constexpr size_t StorageSize
= tl::Max
<sizeof(T1
), sizeof(T2
)>::value
;
47 alignas(StorageAlignment
) unsigned char storage
[StorageSize
];
49 // GCC fails due to -Werror=strict-aliasing if |storage| is directly cast to
50 // T*. Indirecting through these functions addresses the problem.
51 void* data() { return storage
; }
52 const void* data() const { return storage
; }
54 enum State
{ None
, SomeT1
, SomeT2
} state
;
55 template <class T
, class Ignored
= void>
60 MOZ_ASSERT(state
== Type2State
<T
>::result
);
61 return *static_cast<T
*>(data());
66 MOZ_ASSERT(state
== Type2State
<T
>::result
);
67 return *static_cast<const T
*>(data());
71 MaybeOneOf() : state(None
) {}
72 ~MaybeOneOf() { destroyIfConstructed(); }
74 MaybeOneOf(MaybeOneOf
&& rhs
) : state(None
) {
76 if (rhs
.constructed
<T1
>()) {
77 construct
<T1
>(std::move(rhs
.as
<T1
>()));
80 construct
<T2
>(std::move(rhs
.as
<T2
>()));
87 MaybeOneOf
& operator=(MaybeOneOf
&& rhs
) {
88 MOZ_ASSERT(this != &rhs
, "Self-move is prohibited");
90 new (this) MaybeOneOf(std::move(rhs
));
94 bool empty() const { return state
== None
; }
97 bool constructed() const {
98 return state
== Type2State
<T
>::result
;
101 template <class T
, class... Args
>
102 void construct(Args
&&... aArgs
) {
103 MOZ_ASSERT(state
== None
);
104 state
= Type2State
<T
>::result
;
105 ::new (KnownNotNull
, data()) T(std::forward
<Args
>(aArgs
)...);
114 const T
& ref() const {
119 MOZ_ASSERT(state
== SomeT1
|| state
== SomeT2
);
120 if (state
== SomeT1
) {
122 } else if (state
== SomeT2
) {
128 void destroyIfConstructed() {
135 MaybeOneOf(const MaybeOneOf
& aOther
) = delete;
136 const MaybeOneOf
& operator=(const MaybeOneOf
& aOther
) = delete;
139 template <class T1
, class T2
>
140 template <class Ignored
>
141 struct MaybeOneOf
<T1
, T2
>::Type2State
<T1
, Ignored
> {
142 typedef MaybeOneOf
<T1
, T2
> Enclosing
;
143 static const typename
Enclosing::State result
= Enclosing::SomeT1
;
146 template <class T1
, class T2
>
147 template <class Ignored
>
148 struct MaybeOneOf
<T1
, T2
>::Type2State
<T2
, Ignored
> {
149 typedef MaybeOneOf
<T1
, T2
> Enclosing
;
150 static const typename
Enclosing::State result
= Enclosing::SomeT2
;
153 } // namespace mozilla
155 #endif /* mozilla_MaybeOneOf_h */