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
43 static constexpr size_t StorageAlignment
=
44 tl::Max
<alignof(T1
), alignof(T2
)>::value
;
45 static constexpr size_t StorageSize
=
46 tl::Max
<sizeof(T1
), sizeof(T2
)>::value
;
48 alignas(StorageAlignment
) unsigned char storage
[StorageSize
];
50 // GCC fails due to -Werror=strict-aliasing if |storage| is directly cast to
51 // T*. Indirecting through these functions addresses the problem.
52 void* data() { return storage
; }
53 const void* data() const { return storage
; }
55 enum State
{ None
, SomeT1
, SomeT2
} state
;
56 template <class T
, class Ignored
= void> struct Type2State
{};
61 MOZ_ASSERT(state
== Type2State
<T
>::result
);
62 return *static_cast<T
*>(data());
68 MOZ_ASSERT(state
== Type2State
<T
>::result
);
69 return *static_cast<const T
*>(data());
73 MaybeOneOf() : state(None
) {}
74 ~MaybeOneOf() { destroyIfConstructed(); }
76 MaybeOneOf(MaybeOneOf
&& rhs
)
80 if (rhs
.constructed
<T1
>()) {
81 construct
<T1
>(std::move(rhs
.as
<T1
>()));
84 construct
<T2
>(std::move(rhs
.as
<T2
>()));
91 MaybeOneOf
& operator=(MaybeOneOf
&& rhs
)
93 MOZ_ASSERT(this != &rhs
, "Self-move is prohibited");
95 new(this) MaybeOneOf(std::move(rhs
));
99 bool empty() const { return state
== None
; }
102 bool constructed() const { return state
== Type2State
<T
>::result
; }
104 template <class T
, class... Args
>
105 void construct(Args
&&... aArgs
)
107 MOZ_ASSERT(state
== None
);
108 state
= Type2State
<T
>::result
;
109 ::new (KnownNotNull
, data()) T(std::forward
<Args
>(aArgs
)...);
126 MOZ_ASSERT(state
== SomeT1
|| state
== SomeT2
);
127 if (state
== SomeT1
) {
129 } else if (state
== SomeT2
) {
135 void destroyIfConstructed()
143 MaybeOneOf(const MaybeOneOf
& aOther
) = delete;
144 const MaybeOneOf
& operator=(const MaybeOneOf
& aOther
) = delete;
147 template <class T1
, class T2
>
148 template <class Ignored
>
149 struct MaybeOneOf
<T1
, T2
>::Type2State
<T1
, Ignored
>
151 typedef MaybeOneOf
<T1
, T2
> Enclosing
;
152 static const typename
Enclosing::State result
= Enclosing::SomeT1
;
155 template <class T1
, class T2
>
156 template <class Ignored
>
157 struct MaybeOneOf
<T1
, T2
>::Type2State
<T2
, Ignored
>
159 typedef MaybeOneOf
<T1
, T2
> Enclosing
;
160 static const typename
Enclosing::State result
= Enclosing::SomeT2
;
163 } // namespace mozilla
165 #endif /* mozilla_MaybeOneOf_h */