blob: 129f6d9d9716b69529694bd67fc94551717b2a9c [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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
Adam Lesinski803c7c82016-04-06 16:09:43 -070020#include "util/TypeTraits.h"
21
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080022#include <cassert>
23#include <type_traits>
24#include <utility>
25
26namespace 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 */
33template <typename T>
34class Maybe {
35public:
36 /**
37 * Construct Nothing.
38 */
Adam Lesinski24aad162015-04-24 19:19:30 -070039 Maybe();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080040
Adam Lesinski24aad162015-04-24 19:19:30 -070041 ~Maybe();
42
43 Maybe(const Maybe& rhs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080044
45 template <typename U>
Chih-Hung Hsieh470f8fc2016-08-15 12:32:51 -070046 Maybe(const Maybe<U>& rhs); // NOLINT(implicit)
Adam Lesinski24aad162015-04-24 19:19:30 -070047
48 Maybe(Maybe&& rhs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080049
50 template <typename U>
Chih-Hung Hsieh470f8fc2016-08-15 12:32:51 -070051 Maybe(Maybe<U>&& rhs); // NOLINT(implicit)
Adam Lesinski24aad162015-04-24 19:19:30 -070052
53 Maybe& operator=(const Maybe& rhs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054
55 template <typename U>
Adam Lesinski24aad162015-04-24 19:19:30 -070056 Maybe& operator=(const Maybe<U>& rhs);
57
58 Maybe& operator=(Maybe&& rhs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080059
60 template <typename U>
Adam Lesinski24aad162015-04-24 19:19:30 -070061 Maybe& operator=(Maybe<U>&& rhs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080062
63 /**
64 * Construct a Maybe holding a value.
65 */
Chih-Hung Hsieh470f8fc2016-08-15 12:32:51 -070066 Maybe(const T& value); // NOLINT(implicit)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067
68 /**
69 * Construct a Maybe holding a value.
70 */
Chih-Hung Hsieh470f8fc2016-08-15 12:32:51 -070071 Maybe(T&& value); // NOLINT(implicit)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080072
73 /**
74 * True if this holds a value, false if
75 * it holds Nothing.
76 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -070077 explicit operator bool() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080078
79 /**
80 * Gets the value if one exists, or else
81 * panics.
82 */
Adam Lesinski24aad162015-04-24 19:19:30 -070083 T& value();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080084
85 /**
86 * Gets the value if one exists, or else
87 * panics.
88 */
Adam Lesinski24aad162015-04-24 19:19:30 -070089 const T& value() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080090
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -070091 T valueOrDefault(const T& def) const;
92
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080093private:
94 template <typename U>
95 friend class Maybe;
96
Adam Lesinski24aad162015-04-24 19:19:30 -070097 template <typename U>
98 Maybe& copy(const Maybe<U>& rhs);
99
100 template <typename U>
101 Maybe& move(Maybe<U>&& rhs);
102
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800103 void destroy();
104
105 bool mNothing;
106
107 typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
108};
109
110template <typename T>
111Maybe<T>::Maybe()
112: mNothing(true) {
113}
114
115template <typename T>
116Maybe<T>::~Maybe() {
117 if (!mNothing) {
118 destroy();
119 }
120}
121
122template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700123Maybe<T>::Maybe(const Maybe& rhs)
124: mNothing(rhs.mNothing) {
125 if (!rhs.mNothing) {
126 new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
127 }
128}
129
130template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800131template <typename U>
132Maybe<T>::Maybe(const Maybe<U>& rhs)
133: mNothing(rhs.mNothing) {
134 if (!rhs.mNothing) {
135 new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
136 }
137}
138
139template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700140Maybe<T>::Maybe(Maybe&& rhs)
141: mNothing(rhs.mNothing) {
142 if (!rhs.mNothing) {
143 rhs.mNothing = true;
144
145 // Move the value from rhs.
146 new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
147 rhs.destroy();
148 }
149}
150
151template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800152template <typename U>
153Maybe<T>::Maybe(Maybe<U>&& rhs)
154: mNothing(rhs.mNothing) {
155 if (!rhs.mNothing) {
156 rhs.mNothing = true;
157
158 // Move the value from rhs.
159 new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800160 rhs.destroy();
161 }
162}
163
164template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700165inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
166 // Delegate to the actual assignment.
167 return copy(rhs);
168}
169
170template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800171template <typename U>
Adam Lesinski24aad162015-04-24 19:19:30 -0700172inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
173 return copy(rhs);
174}
175
176template <typename T>
177template <typename U>
178Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800179 if (mNothing && rhs.mNothing) {
180 // Both are nothing, nothing to do.
181 return *this;
182 } else if (!mNothing && !rhs.mNothing) {
183 // We both are something, so assign rhs to us.
184 reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
185 } else if (mNothing) {
186 // We are nothing but rhs is something.
187 mNothing = rhs.mNothing;
188
189 // Copy the value from rhs.
190 new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
191 } else {
192 // We are something but rhs is nothing, so destroy our value.
193 mNothing = rhs.mNothing;
194 destroy();
195 }
196 return *this;
197}
198
199template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700200inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
201 // Delegate to the actual assignment.
202 return move(std::forward<Maybe<T>>(rhs));
203}
204
205template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800206template <typename U>
Adam Lesinski24aad162015-04-24 19:19:30 -0700207inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
208 return move(std::forward<Maybe<U>>(rhs));
209}
210
211template <typename T>
212template <typename U>
213Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800214 if (mNothing && rhs.mNothing) {
215 // Both are nothing, nothing to do.
216 return *this;
217 } else if (!mNothing && !rhs.mNothing) {
218 // We both are something, so move assign rhs to us.
219 rhs.mNothing = true;
220 reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
221 rhs.destroy();
222 } else if (mNothing) {
223 // We are nothing but rhs is something.
Adam Lesinski24aad162015-04-24 19:19:30 -0700224 mNothing = false;
225 rhs.mNothing = true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800226
227 // Move the value from rhs.
228 new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
229 rhs.destroy();
230 } else {
231 // We are something but rhs is nothing, so destroy our value.
Adam Lesinski24aad162015-04-24 19:19:30 -0700232 mNothing = true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800233 destroy();
234 }
235 return *this;
236}
237
238template <typename T>
239Maybe<T>::Maybe(const T& value)
240: mNothing(false) {
241 new (&mStorage) T(value);
242}
243
244template <typename T>
245Maybe<T>::Maybe(T&& value)
246: mNothing(false) {
247 new (&mStorage) T(std::forward<T>(value));
248}
249
250template <typename T>
251Maybe<T>::operator bool() const {
252 return !mNothing;
253}
254
255template <typename T>
256T& Maybe<T>::value() {
257 assert(!mNothing && "Maybe<T>::value() called on Nothing");
258 return reinterpret_cast<T&>(mStorage);
259}
260
261template <typename T>
262const T& Maybe<T>::value() const {
263 assert(!mNothing && "Maybe<T>::value() called on Nothing");
264 return reinterpret_cast<const T&>(mStorage);
265}
266
267template <typename T>
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700268T Maybe<T>::valueOrDefault(const T& def) const {
269 if (mNothing) {
270 return def;
271 }
272 return reinterpret_cast<const T&>(mStorage);
273}
274
275template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800276void Maybe<T>::destroy() {
277 reinterpret_cast<T&>(mStorage).~T();
278}
279
280template <typename T>
281inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
282 return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
283}
284
285template <typename T>
286inline Maybe<T> make_nothing() {
287 return Maybe<T>();
288}
289
Adam Lesinskia5870652015-11-20 15:32:30 -0800290/**
Adam Lesinski803c7c82016-04-06 16:09:43 -0700291 * Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
292 * That way the compiler will show an error at the callsite when comparing two Maybe<> objects
293 * whose inner types can't be compared.
Adam Lesinskia5870652015-11-20 15:32:30 -0800294 */
295template <typename T, typename U>
Adam Lesinski803c7c82016-04-06 16:09:43 -0700296typename std::enable_if<
297 has_eq_op<T, U>::value,
298 bool
299>::type operator==(const Maybe<T>& a, const Maybe<U>& b) {
Adam Lesinskia5870652015-11-20 15:32:30 -0800300 if (a && b) {
301 return a.value() == b.value();
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800302 } else if (!a && !b) {
303 return true;
Adam Lesinskia5870652015-11-20 15:32:30 -0800304 }
305 return false;
306}
307
308/**
309 * Same as operator== but negated.
310 */
311template <typename T, typename U>
Adam Lesinski803c7c82016-04-06 16:09:43 -0700312typename std::enable_if<
313 has_eq_op<T, U>::value,
314 bool
315>::type operator!=(const Maybe<T>& a, const Maybe<U>& b) {
Adam Lesinskia5870652015-11-20 15:32:30 -0800316 return !(a == b);
317}
318
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700319template <typename T, typename U>
320typename std::enable_if<
321 has_lt_op<T, U>::value,
322 bool
323>::type operator<(const Maybe<T>& a, const Maybe<U>& b) {
324 if (a && b) {
325 return a.value() < b.value();
326 } else if (!a && !b) {
327 return false;
328 }
329 return !a;
330}
331
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800332} // namespace aapt
333
334#endif // AAPT_MAYBE_H