blob: f6a396de1bec76eced04cd29234004593a5b7a36 [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
20#include <cassert>
21#include <type_traits>
22#include <utility>
23
24namespace aapt {
25
26/**
27 * Either holds a valid value of type T, or holds Nothing.
28 * The value is stored inline in this structure, so no
29 * heap memory is used when creating a Maybe<T> object.
30 */
31template <typename T>
32class Maybe {
33public:
34 /**
35 * Construct Nothing.
36 */
37 inline Maybe();
38
39 inline ~Maybe();
40
41 template <typename U>
42 inline Maybe(const Maybe<U>& rhs);
43
44 template <typename U>
45 inline Maybe(Maybe<U>&& rhs);
46
47 template <typename U>
48 inline Maybe& operator=(const Maybe<U>& rhs);
49
50 template <typename U>
51 inline Maybe& operator=(Maybe<U>&& rhs);
52
53 /**
54 * Construct a Maybe holding a value.
55 */
56 inline Maybe(const T& value);
57
58 /**
59 * Construct a Maybe holding a value.
60 */
61 inline Maybe(T&& value);
62
63 /**
64 * True if this holds a value, false if
65 * it holds Nothing.
66 */
67 inline operator bool() const;
68
69 /**
70 * Gets the value if one exists, or else
71 * panics.
72 */
73 inline T& value();
74
75 /**
76 * Gets the value if one exists, or else
77 * panics.
78 */
79 inline const T& value() const;
80
81private:
82 template <typename U>
83 friend class Maybe;
84
85 void destroy();
86
87 bool mNothing;
88
89 typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
90};
91
92template <typename T>
93Maybe<T>::Maybe()
94: mNothing(true) {
95}
96
97template <typename T>
98Maybe<T>::~Maybe() {
99 if (!mNothing) {
100 destroy();
101 }
102}
103
104template <typename T>
105template <typename U>
106Maybe<T>::Maybe(const Maybe<U>& rhs)
107: mNothing(rhs.mNothing) {
108 if (!rhs.mNothing) {
109 new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
110 }
111}
112
113template <typename T>
114template <typename U>
115Maybe<T>::Maybe(Maybe<U>&& rhs)
116: mNothing(rhs.mNothing) {
117 if (!rhs.mNothing) {
118 rhs.mNothing = true;
119
120 // Move the value from rhs.
121 new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
122
123 // Since the value in rhs is now Nothing,
124 // run the destructor for the value.
125 rhs.destroy();
126 }
127}
128
129template <typename T>
130template <typename U>
131Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
132 if (mNothing && rhs.mNothing) {
133 // Both are nothing, nothing to do.
134 return *this;
135 } else if (!mNothing && !rhs.mNothing) {
136 // We both are something, so assign rhs to us.
137 reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
138 } else if (mNothing) {
139 // We are nothing but rhs is something.
140 mNothing = rhs.mNothing;
141
142 // Copy the value from rhs.
143 new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
144 } else {
145 // We are something but rhs is nothing, so destroy our value.
146 mNothing = rhs.mNothing;
147 destroy();
148 }
149 return *this;
150}
151
152template <typename T>
153template <typename U>
154Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
155 if (mNothing && rhs.mNothing) {
156 // Both are nothing, nothing to do.
157 return *this;
158 } else if (!mNothing && !rhs.mNothing) {
159 // We both are something, so move assign rhs to us.
160 rhs.mNothing = true;
161 reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
162 rhs.destroy();
163 } else if (mNothing) {
164 // We are nothing but rhs is something.
165 mNothing = rhs.mNothing;
166
167 // Move the value from rhs.
168 new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
169 rhs.destroy();
170 } else {
171 // We are something but rhs is nothing, so destroy our value.
172 mNothing = rhs.mNothing;
173 destroy();
174 }
175 return *this;
176}
177
178template <typename T>
179Maybe<T>::Maybe(const T& value)
180: mNothing(false) {
181 new (&mStorage) T(value);
182}
183
184template <typename T>
185Maybe<T>::Maybe(T&& value)
186: mNothing(false) {
187 new (&mStorage) T(std::forward<T>(value));
188}
189
190template <typename T>
191Maybe<T>::operator bool() const {
192 return !mNothing;
193}
194
195template <typename T>
196T& Maybe<T>::value() {
197 assert(!mNothing && "Maybe<T>::value() called on Nothing");
198 return reinterpret_cast<T&>(mStorage);
199}
200
201template <typename T>
202const T& Maybe<T>::value() const {
203 assert(!mNothing && "Maybe<T>::value() called on Nothing");
204 return reinterpret_cast<const T&>(mStorage);
205}
206
207template <typename T>
208void Maybe<T>::destroy() {
209 reinterpret_cast<T&>(mStorage).~T();
210}
211
212template <typename T>
213inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
214 return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
215}
216
217template <typename T>
218inline Maybe<T> make_nothing() {
219 return Maybe<T>();
220}
221
222} // namespace aapt
223
224#endif // AAPT_MAYBE_H