blob: 595db960d5e5e4cb83de691afe42113905017b2d [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>
Adam Lesinski24aad162015-04-24 19:19:30 -070046 Maybe(const Maybe<U>& rhs);
47
48 Maybe(Maybe&& rhs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080049
50 template <typename U>
Adam Lesinski24aad162015-04-24 19:19:30 -070051 Maybe(Maybe<U>&& rhs);
52
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 */
Adam Lesinski24aad162015-04-24 19:19:30 -070066 Maybe(const T& value);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067
68 /**
69 * Construct a Maybe holding a value.
70 */
Adam Lesinski24aad162015-04-24 19:19:30 -070071 Maybe(T&& value);
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
91private:
92 template <typename U>
93 friend class Maybe;
94
Adam Lesinski24aad162015-04-24 19:19:30 -070095 template <typename U>
96 Maybe& copy(const Maybe<U>& rhs);
97
98 template <typename U>
99 Maybe& move(Maybe<U>&& rhs);
100
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800101 void destroy();
102
103 bool mNothing;
104
105 typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
106};
107
108template <typename T>
109Maybe<T>::Maybe()
110: mNothing(true) {
111}
112
113template <typename T>
114Maybe<T>::~Maybe() {
115 if (!mNothing) {
116 destroy();
117 }
118}
119
120template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700121Maybe<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
128template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800129template <typename U>
130Maybe<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
137template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700138Maybe<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
149template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800150template <typename U>
151Maybe<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)));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800158 rhs.destroy();
159 }
160}
161
162template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700163inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
164 // Delegate to the actual assignment.
165 return copy(rhs);
166}
167
168template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800169template <typename U>
Adam Lesinski24aad162015-04-24 19:19:30 -0700170inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
171 return copy(rhs);
172}
173
174template <typename T>
175template <typename U>
176Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800177 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
197template <typename T>
Adam Lesinski24aad162015-04-24 19:19:30 -0700198inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
199 // Delegate to the actual assignment.
200 return move(std::forward<Maybe<T>>(rhs));
201}
202
203template <typename T>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800204template <typename U>
Adam Lesinski24aad162015-04-24 19:19:30 -0700205inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
206 return move(std::forward<Maybe<U>>(rhs));
207}
208
209template <typename T>
210template <typename U>
211Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800212 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.
Adam Lesinski24aad162015-04-24 19:19:30 -0700222 mNothing = false;
223 rhs.mNothing = true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800224
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.
Adam Lesinski24aad162015-04-24 19:19:30 -0700230 mNothing = true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800231 destroy();
232 }
233 return *this;
234}
235
236template <typename T>
237Maybe<T>::Maybe(const T& value)
238: mNothing(false) {
239 new (&mStorage) T(value);
240}
241
242template <typename T>
243Maybe<T>::Maybe(T&& value)
244: mNothing(false) {
245 new (&mStorage) T(std::forward<T>(value));
246}
247
248template <typename T>
249Maybe<T>::operator bool() const {
250 return !mNothing;
251}
252
253template <typename T>
254T& Maybe<T>::value() {
255 assert(!mNothing && "Maybe<T>::value() called on Nothing");
256 return reinterpret_cast<T&>(mStorage);
257}
258
259template <typename T>
260const T& Maybe<T>::value() const {
261 assert(!mNothing && "Maybe<T>::value() called on Nothing");
262 return reinterpret_cast<const T&>(mStorage);
263}
264
265template <typename T>
266void Maybe<T>::destroy() {
267 reinterpret_cast<T&>(mStorage).~T();
268}
269
270template <typename T>
271inline 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
275template <typename T>
276inline Maybe<T> make_nothing() {
277 return Maybe<T>();
278}
279
Adam Lesinskia5870652015-11-20 15:32:30 -0800280/**
Adam Lesinski803c7c82016-04-06 16:09:43 -0700281 * 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.
Adam Lesinskia5870652015-11-20 15:32:30 -0800284 */
285template <typename T, typename U>
Adam Lesinski803c7c82016-04-06 16:09:43 -0700286typename std::enable_if<
287 has_eq_op<T, U>::value,
288 bool
289>::type operator==(const Maybe<T>& a, const Maybe<U>& b) {
Adam Lesinskia5870652015-11-20 15:32:30 -0800290 if (a && b) {
291 return a.value() == b.value();
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800292 } else if (!a && !b) {
293 return true;
Adam Lesinskia5870652015-11-20 15:32:30 -0800294 }
295 return false;
296}
297
298/**
299 * Same as operator== but negated.
300 */
301template <typename T, typename U>
Adam Lesinski803c7c82016-04-06 16:09:43 -0700302typename std::enable_if<
303 has_eq_op<T, U>::value,
304 bool
305>::type operator!=(const Maybe<T>& a, const Maybe<U>& b) {
Adam Lesinskia5870652015-11-20 15:32:30 -0800306 return !(a == b);
307}
308
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800309} // namespace aapt
310
311#endif // AAPT_MAYBE_H