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 "mozilla/Assertions.h"
16 #include "mozilla/Move.h"
17 #include "mozilla/OperatorNewExtensions.h"
18 #include "mozilla/TemplateLib.h"
20 #include <new> // for placement new
21 #include <stddef.h> // for size_t
26 * MaybeOneOf<T1, T2> is like Maybe, but it supports constructing either T1
27 * or T2. When a MaybeOneOf<T1, T2> is constructed, it is |empty()|, i.e.,
28 * no value has been constructed and no destructor will be called when the
29 * MaybeOneOf<T1, T2> is destroyed. Upon calling |construct<T1>()| or
30 * |construct<T2>()|, a T1 or T2 object will be constructed with the given
31 * arguments and that object will be destroyed when the owning MaybeOneOf is
34 * Because MaybeOneOf must be aligned suitable to hold any value stored within
35 * it, and because |alignas| requirements don't affect platform ABI with respect
36 * to how parameters are laid out in memory, MaybeOneOf can't be used as the
37 * type of a function parameter. Pass MaybeOneOf to functions by pointer or
40 template <class T1
, class T2
>
41 class MOZ_NON_PARAM MaybeOneOf
{
42 static constexpr size_t StorageAlignment
=
43 tl::Max
<alignof(T1
), alignof(T2
)>::value
;
44 static constexpr size_t StorageSize
= tl::Max
<sizeof(T1
), sizeof(T2
)>::value
;
46 alignas(StorageAlignment
) unsigned char storage
[StorageSize
];
48 // GCC fails due to -Werror=strict-aliasing if |storage| is directly cast to
49 // T*. Indirecting through these functions addresses the problem.
50 void* data() { return storage
; }
51 const void* data() const { return storage
; }
53 enum State
{ None
, SomeT1
, SomeT2
} state
;
54 template <class T
, class Ignored
= void>
59 MOZ_ASSERT(state
== Type2State
<T
>::result
);
60 return *static_cast<T
*>(data());
65 MOZ_ASSERT(state
== Type2State
<T
>::result
);
66 return *static_cast<const T
*>(data());
70 MaybeOneOf() : state(None
) {}
71 ~MaybeOneOf() { destroyIfConstructed(); }
73 MaybeOneOf(MaybeOneOf
&& rhs
) : state(None
) {
75 if (rhs
.constructed
<T1
>()) {
76 construct
<T1
>(std::move(rhs
.as
<T1
>()));
79 construct
<T2
>(std::move(rhs
.as
<T2
>()));
86 MaybeOneOf
& operator=(MaybeOneOf
&& rhs
) {
87 MOZ_ASSERT(this != &rhs
, "Self-move is prohibited");
89 new (this) MaybeOneOf(std::move(rhs
));
93 bool empty() const { return state
== None
; }
96 bool constructed() const {
97 return state
== Type2State
<T
>::result
;
100 template <class T
, class... Args
>
101 void construct(Args
&&... aArgs
) {
102 MOZ_ASSERT(state
== None
);
103 state
= Type2State
<T
>::result
;
104 ::new (KnownNotNull
, data()) T(std::forward
<Args
>(aArgs
)...);
113 const T
& ref() const {
118 MOZ_ASSERT(state
== SomeT1
|| state
== SomeT2
);
119 if (state
== SomeT1
) {
121 } else if (state
== SomeT2
) {
127 void destroyIfConstructed() {
134 MaybeOneOf(const MaybeOneOf
& aOther
) = delete;
135 const MaybeOneOf
& operator=(const MaybeOneOf
& aOther
) = delete;
138 template <class T1
, class T2
>
139 template <class Ignored
>
140 struct MaybeOneOf
<T1
, T2
>::Type2State
<T1
, Ignored
> {
141 typedef MaybeOneOf
<T1
, T2
> Enclosing
;
142 static const typename
Enclosing::State result
= Enclosing::SomeT1
;
145 template <class T1
, class T2
>
146 template <class Ignored
>
147 struct MaybeOneOf
<T1
, T2
>::Type2State
<T2
, Ignored
> {
148 typedef MaybeOneOf
<T1
, T2
> Enclosing
;
149 static const typename
Enclosing::State result
= Enclosing::SomeT2
;
152 } // namespace mozilla
154 #endif /* mozilla_MaybeOneOf_h */