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 <type_traits>
21 #include <utility>
22
23 #include "android-base/logging.h"
24
25 #include "util/TypeTraits.h"
26
27 namespace aapt {
28
29 /**
30 * Either holds a valid value of type T, or holds Nothing.
31 * The value is stored inline in this structure, so no
32 * heap memory is used when creating a Maybe<T> object.
33 */
34 template <typename T>
35 class Maybe {
36 public:
37 /**
38 * Construct Nothing.
39 */
40 Maybe();
41
42 ~Maybe();
43
44 Maybe(const Maybe& rhs);
45
46 template <typename U>
47 Maybe(const Maybe<U>& rhs); // NOLINT(implicit)
48
49 Maybe(Maybe&& rhs);
50
51 template <typename U>
52 Maybe(Maybe<U>&& rhs); // NOLINT(implicit)
53
54 Maybe& operator=(const Maybe& rhs);
55
56 template <typename U>
57 Maybe& operator=(const Maybe<U>& rhs);
58
59 Maybe& operator=(Maybe&& rhs);
60
61 template <typename U>
62 Maybe& operator=(Maybe<U>&& rhs);
63
64 /**
65 * Construct a Maybe holding a value.
66 */
67 Maybe(const T& value); // NOLINT(implicit)
68
69 /**
70 * Construct a Maybe holding a value.
71 */
72 Maybe(T&& value); // NOLINT(implicit)
73
74 /**
75 * True if this holds a value, false if
76 * it holds Nothing.
77 */
78 explicit operator bool() const;
79
80 /**
81 * Gets the value if one exists, or else
82 * panics.
83 */
84 T& value();
85
86 /**
87 * Gets the value if one exists, or else
88 * panics.
89 */
90 const T& value() const;
91
92 T value_or_default(const T& def) const;
93
94 private:
95 template <typename U>
96 friend class Maybe;
97
98 template <typename U>
99 Maybe& copy(const Maybe<U>& rhs);
100
101 template <typename U>
102 Maybe& move(Maybe<U>&& rhs);
103
104 void destroy();
105
106 bool nothing_;
107
108 typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
109 };
110
111 template <typename T>
Maybe()112 Maybe<T>::Maybe() : nothing_(true) {}
113
114 template <typename T>
~Maybe()115 Maybe<T>::~Maybe() {
116 if (!nothing_) {
117 destroy();
118 }
119 }
120
121 template <typename T>
Maybe(const Maybe & rhs)122 Maybe<T>::Maybe(const Maybe& rhs) : nothing_(rhs.nothing_) {
123 if (!rhs.nothing_) {
124 new (&storage_) T(reinterpret_cast<const T&>(rhs.storage_));
125 }
126 }
127
128 template <typename T>
129 template <typename U>
Maybe(const Maybe<U> & rhs)130 Maybe<T>::Maybe(const Maybe<U>& rhs) : nothing_(rhs.nothing_) {
131 if (!rhs.nothing_) {
132 new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
133 }
134 }
135
136 template <typename T>
Maybe(Maybe && rhs)137 Maybe<T>::Maybe(Maybe&& rhs) : nothing_(rhs.nothing_) {
138 if (!rhs.nothing_) {
139 rhs.nothing_ = true;
140
141 // Move the value from rhs.
142 new (&storage_) T(std::move(reinterpret_cast<T&>(rhs.storage_)));
143 rhs.destroy();
144 }
145 }
146
147 template <typename T>
148 template <typename U>
Maybe(Maybe<U> && rhs)149 Maybe<T>::Maybe(Maybe<U>&& rhs) : nothing_(rhs.nothing_) {
150 if (!rhs.nothing_) {
151 rhs.nothing_ = true;
152
153 // Move the value from rhs.
154 new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
155 rhs.destroy();
156 }
157 }
158
159 template <typename T>
160 inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
161 // Delegate to the actual assignment.
162 return copy(rhs);
163 }
164
165 template <typename T>
166 template <typename U>
167 inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
168 return copy(rhs);
169 }
170
171 template <typename T>
172 template <typename U>
copy(const Maybe<U> & rhs)173 Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
174 if (nothing_ && rhs.nothing_) {
175 // Both are nothing, nothing to do.
176 return *this;
177 } else if (!nothing_ && !rhs.nothing_) {
178 // We both are something, so assign rhs to us.
179 reinterpret_cast<T&>(storage_) = reinterpret_cast<const U&>(rhs.storage_);
180 } else if (nothing_) {
181 // We are nothing but rhs is something.
182 nothing_ = rhs.nothing_;
183
184 // Copy the value from rhs.
185 new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
186 } else {
187 // We are something but rhs is nothing, so destroy our value.
188 nothing_ = rhs.nothing_;
189 destroy();
190 }
191 return *this;
192 }
193
194 template <typename T>
195 inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
196 // Delegate to the actual assignment.
197 return move(std::forward<Maybe<T>>(rhs));
198 }
199
200 template <typename T>
201 template <typename U>
202 inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
203 return move(std::forward<Maybe<U>>(rhs));
204 }
205
206 template <typename T>
207 template <typename U>
move(Maybe<U> && rhs)208 Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
209 if (nothing_ && rhs.nothing_) {
210 // Both are nothing, nothing to do.
211 return *this;
212 } else if (!nothing_ && !rhs.nothing_) {
213 // We both are something, so move assign rhs to us.
214 rhs.nothing_ = true;
215 reinterpret_cast<T&>(storage_) =
216 std::move(reinterpret_cast<U&>(rhs.storage_));
217 rhs.destroy();
218 } else if (nothing_) {
219 // We are nothing but rhs is something.
220 nothing_ = false;
221 rhs.nothing_ = true;
222
223 // Move the value from rhs.
224 new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
225 rhs.destroy();
226 } else {
227 // We are something but rhs is nothing, so destroy our value.
228 nothing_ = true;
229 destroy();
230 }
231 return *this;
232 }
233
234 template <typename T>
Maybe(const T & value)235 Maybe<T>::Maybe(const T& value) : nothing_(false) {
236 new (&storage_) T(value);
237 }
238
239 template <typename T>
Maybe(T && value)240 Maybe<T>::Maybe(T&& value) : nothing_(false) {
241 new (&storage_) T(std::forward<T>(value));
242 }
243
244 template <typename T>
245 Maybe<T>::operator bool() const {
246 return !nothing_;
247 }
248
249 template <typename T>
value()250 T& Maybe<T>::value() {
251 CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
252 return reinterpret_cast<T&>(storage_);
253 }
254
255 template <typename T>
value()256 const T& Maybe<T>::value() const {
257 CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
258 return reinterpret_cast<const T&>(storage_);
259 }
260
261 template <typename T>
value_or_default(const T & def)262 T Maybe<T>::value_or_default(const T& def) const {
263 if (nothing_) {
264 return def;
265 }
266 return reinterpret_cast<const T&>(storage_);
267 }
268
269 template <typename T>
destroy()270 void Maybe<T>::destroy() {
271 reinterpret_cast<T&>(storage_).~T();
272 }
273
274 template <typename T>
make_value(T && value)275 inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
276 return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
277 }
278
279 template <typename T>
make_nothing()280 inline Maybe<T> make_nothing() {
281 return Maybe<T>();
282 }
283
284 // Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
285 // That way the compiler will show an error at the callsite when comparing two Maybe<> objects
286 // whose inner types can't be compared.
287 template <typename T, typename U>
288 typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
289 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 template <typename T, typename U>
299 typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
300 const U& b) {
301 return a ? a.value() == b : false;
302 }
303
304 // Same as operator== but negated.
305 template <typename T, typename U>
306 typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(const Maybe<T>& a,
307 const Maybe<U>& b) {
308 return !(a == b);
309 }
310
311 template <typename T, typename U>
312 typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(const Maybe<T>& a,
313 const Maybe<U>& b) {
314 if (a && b) {
315 return a.value() < b.value();
316 } else if (!a && !b) {
317 return false;
318 }
319 return !a;
320 }
321
322 } // namespace aapt
323
324 #endif // AAPT_MAYBE_H
325