1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef AAPT_MAYBE_H
18 #define AAPT_MAYBE_H
19
20 #include "util/TypeTraits.h"
21
22 #include <cassert>
23 #include <type_traits>
24 #include <utility>
25
26 namespace aapt {
27
28 /**
29 * Either holds a valid value of type T, or holds Nothing.
30 * The value is stored inline in this structure, so no
31 * heap memory is used when creating a Maybe<T> object.
32 */
33 template <typename T>
34 class Maybe {
35 public:
36 /**
37 * Construct Nothing.
38 */
39 Maybe();
40
41 ~Maybe();
42
43 Maybe(const Maybe& rhs);
44
45 template <typename U>
46 Maybe(const Maybe<U>& rhs);
47
48 Maybe(Maybe&& rhs);
49
50 template <typename U>
51 Maybe(Maybe<U>&& rhs);
52
53 Maybe& operator=(const Maybe& rhs);
54
55 template <typename U>
56 Maybe& operator=(const Maybe<U>& rhs);
57
58 Maybe& operator=(Maybe&& rhs);
59
60 template <typename U>
61 Maybe& operator=(Maybe<U>&& rhs);
62
63 /**
64 * Construct a Maybe holding a value.
65 */
66 Maybe(const T& value);
67
68 /**
69 * Construct a Maybe holding a value.
70 */
71 Maybe(T&& value);
72
73 /**
74 * True if this holds a value, false if
75 * it holds Nothing.
76 */
77 explicit operator bool() const;
78
79 /**
80 * Gets the value if one exists, or else
81 * panics.
82 */
83 T& value();
84
85 /**
86 * Gets the value if one exists, or else
87 * panics.
88 */
89 const T& value() const;
90
91 private:
92 template <typename U>
93 friend class Maybe;
94
95 template <typename U>
96 Maybe& copy(const Maybe<U>& rhs);
97
98 template <typename U>
99 Maybe& move(Maybe<U>&& rhs);
100
101 void destroy();
102
103 bool mNothing;
104
105 typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
106 };
107
108 template <typename T>
Maybe()109 Maybe<T>::Maybe()
110 : mNothing(true) {
111 }
112
113 template <typename T>
~Maybe()114 Maybe<T>::~Maybe() {
115 if (!mNothing) {
116 destroy();
117 }
118 }
119
120 template <typename T>
Maybe(const Maybe & rhs)121 Maybe<T>::Maybe(const Maybe& rhs)
122 : mNothing(rhs.mNothing) {
123 if (!rhs.mNothing) {
124 new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
125 }
126 }
127
128 template <typename T>
129 template <typename U>
Maybe(const Maybe<U> & rhs)130 Maybe<T>::Maybe(const Maybe<U>& rhs)
131 : mNothing(rhs.mNothing) {
132 if (!rhs.mNothing) {
133 new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
134 }
135 }
136
137 template <typename T>
Maybe(Maybe && rhs)138 Maybe<T>::Maybe(Maybe&& rhs)
139 : mNothing(rhs.mNothing) {
140 if (!rhs.mNothing) {
141 rhs.mNothing = true;
142
143 // Move the value from rhs.
144 new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
145 rhs.destroy();
146 }
147 }
148
149 template <typename T>
150 template <typename U>
Maybe(Maybe<U> && rhs)151 Maybe<T>::Maybe(Maybe<U>&& rhs)
152 : mNothing(rhs.mNothing) {
153 if (!rhs.mNothing) {
154 rhs.mNothing = true;
155
156 // Move the value from rhs.
157 new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
158 rhs.destroy();
159 }
160 }
161
162 template <typename T>
163 inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
164 // Delegate to the actual assignment.
165 return copy(rhs);
166 }
167
168 template <typename T>
169 template <typename U>
170 inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
171 return copy(rhs);
172 }
173
174 template <typename T>
175 template <typename U>
copy(const Maybe<U> & rhs)176 Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
177 if (mNothing && rhs.mNothing) {
178 // Both are nothing, nothing to do.
179 return *this;
180 } else if (!mNothing && !rhs.mNothing) {
181 // We both are something, so assign rhs to us.
182 reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
183 } else if (mNothing) {
184 // We are nothing but rhs is something.
185 mNothing = rhs.mNothing;
186
187 // Copy the value from rhs.
188 new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
189 } else {
190 // We are something but rhs is nothing, so destroy our value.
191 mNothing = rhs.mNothing;
192 destroy();
193 }
194 return *this;
195 }
196
197 template <typename T>
198 inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
199 // Delegate to the actual assignment.
200 return move(std::forward<Maybe<T>>(rhs));
201 }
202
203 template <typename T>
204 template <typename U>
205 inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
206 return move(std::forward<Maybe<U>>(rhs));
207 }
208
209 template <typename T>
210 template <typename U>
move(Maybe<U> && rhs)211 Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
212 if (mNothing && rhs.mNothing) {
213 // Both are nothing, nothing to do.
214 return *this;
215 } else if (!mNothing && !rhs.mNothing) {
216 // We both are something, so move assign rhs to us.
217 rhs.mNothing = true;
218 reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
219 rhs.destroy();
220 } else if (mNothing) {
221 // We are nothing but rhs is something.
222 mNothing = false;
223 rhs.mNothing = true;
224
225 // Move the value from rhs.
226 new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
227 rhs.destroy();
228 } else {
229 // We are something but rhs is nothing, so destroy our value.
230 mNothing = true;
231 destroy();
232 }
233 return *this;
234 }
235
236 template <typename T>
Maybe(const T & value)237 Maybe<T>::Maybe(const T& value)
238 : mNothing(false) {
239 new (&mStorage) T(value);
240 }
241
242 template <typename T>
Maybe(T && value)243 Maybe<T>::Maybe(T&& value)
244 : mNothing(false) {
245 new (&mStorage) T(std::forward<T>(value));
246 }
247
248 template <typename T>
249 Maybe<T>::operator bool() const {
250 return !mNothing;
251 }
252
253 template <typename T>
value()254 T& Maybe<T>::value() {
255 assert(!mNothing && "Maybe<T>::value() called on Nothing");
256 return reinterpret_cast<T&>(mStorage);
257 }
258
259 template <typename T>
value()260 const T& Maybe<T>::value() const {
261 assert(!mNothing && "Maybe<T>::value() called on Nothing");
262 return reinterpret_cast<const T&>(mStorage);
263 }
264
265 template <typename T>
destroy()266 void Maybe<T>::destroy() {
267 reinterpret_cast<T&>(mStorage).~T();
268 }
269
270 template <typename T>
make_value(T && value)271 inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
272 return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
273 }
274
275 template <typename T>
make_nothing()276 inline Maybe<T> make_nothing() {
277 return Maybe<T>();
278 }
279
280 /**
281 * Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
282 * That way the compiler will show an error at the callsite when comparing two Maybe<> objects
283 * whose inner types can't be compared.
284 */
285 template <typename T, typename U>
286 typename std::enable_if<
287 has_eq_op<T, U>::value,
288 bool
289 >::type operator==(const Maybe<T>& a, const Maybe<U>& b) {
290 if (a && b) {
291 return a.value() == b.value();
292 } else if (!a && !b) {
293 return true;
294 }
295 return false;
296 }
297
298 /**
299 * Same as operator== but negated.
300 */
301 template <typename T, typename U>
302 typename std::enable_if<
303 has_eq_op<T, U>::value,
304 bool
305 >::type operator!=(const Maybe<T>& a, const Maybe<U>& b) {
306 return !(a == b);
307 }
308
309 } // namespace aapt
310
311 #endif // AAPT_MAYBE_H
312