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 <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    Maybe();
38
39    ~Maybe();
40
41    Maybe(const Maybe& rhs);
42
43    template <typename U>
44    Maybe(const Maybe<U>& rhs);
45
46    Maybe(Maybe&& rhs);
47
48    template <typename U>
49    Maybe(Maybe<U>&& rhs);
50
51    Maybe& operator=(const Maybe& rhs);
52
53    template <typename U>
54    Maybe& operator=(const Maybe<U>& rhs);
55
56    Maybe& operator=(Maybe&& rhs);
57
58    template <typename U>
59    Maybe& operator=(Maybe<U>&& rhs);
60
61    /**
62     * Construct a Maybe holding a value.
63     */
64    Maybe(const T& value);
65
66    /**
67     * Construct a Maybe holding a value.
68     */
69    Maybe(T&& value);
70
71    /**
72     * True if this holds a value, false if
73     * it holds Nothing.
74     */
75    operator bool() const;
76
77    /**
78     * Gets the value if one exists, or else
79     * panics.
80     */
81    T& value();
82
83    /**
84     * Gets the value if one exists, or else
85     * panics.
86     */
87    const T& value() const;
88
89private:
90    template <typename U>
91    friend class Maybe;
92
93    template <typename U>
94    Maybe& copy(const Maybe<U>& rhs);
95
96    template <typename U>
97    Maybe& move(Maybe<U>&& rhs);
98
99    void destroy();
100
101    bool mNothing;
102
103    typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
104};
105
106template <typename T>
107Maybe<T>::Maybe()
108: mNothing(true) {
109}
110
111template <typename T>
112Maybe<T>::~Maybe() {
113    if (!mNothing) {
114        destroy();
115    }
116}
117
118template <typename T>
119Maybe<T>::Maybe(const Maybe& rhs)
120: mNothing(rhs.mNothing) {
121    if (!rhs.mNothing) {
122        new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
123    }
124}
125
126template <typename T>
127template <typename U>
128Maybe<T>::Maybe(const Maybe<U>& rhs)
129: mNothing(rhs.mNothing) {
130    if (!rhs.mNothing) {
131        new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
132    }
133}
134
135template <typename T>
136Maybe<T>::Maybe(Maybe&& rhs)
137: mNothing(rhs.mNothing) {
138    if (!rhs.mNothing) {
139        rhs.mNothing = true;
140
141        // Move the value from rhs.
142        new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
143        rhs.destroy();
144    }
145}
146
147template <typename T>
148template <typename U>
149Maybe<T>::Maybe(Maybe<U>&& rhs)
150: mNothing(rhs.mNothing) {
151    if (!rhs.mNothing) {
152        rhs.mNothing = true;
153
154        // Move the value from rhs.
155        new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
156        rhs.destroy();
157    }
158}
159
160template <typename T>
161inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
162    // Delegate to the actual assignment.
163    return copy(rhs);
164}
165
166template <typename T>
167template <typename U>
168inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
169    return copy(rhs);
170}
171
172template <typename T>
173template <typename U>
174Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
175    if (mNothing && rhs.mNothing) {
176        // Both are nothing, nothing to do.
177        return *this;
178    } else if  (!mNothing && !rhs.mNothing) {
179        // We both are something, so assign rhs to us.
180        reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
181    } else if (mNothing) {
182        // We are nothing but rhs is something.
183        mNothing = rhs.mNothing;
184
185        // Copy the value from rhs.
186        new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
187    } else {
188        // We are something but rhs is nothing, so destroy our value.
189        mNothing = rhs.mNothing;
190        destroy();
191    }
192    return *this;
193}
194
195template <typename T>
196inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
197    // Delegate to the actual assignment.
198    return move(std::forward<Maybe<T>>(rhs));
199}
200
201template <typename T>
202template <typename U>
203inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
204    return move(std::forward<Maybe<U>>(rhs));
205}
206
207template <typename T>
208template <typename U>
209Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
210    if (mNothing && rhs.mNothing) {
211        // Both are nothing, nothing to do.
212        return *this;
213    } else if  (!mNothing && !rhs.mNothing) {
214        // We both are something, so move assign rhs to us.
215        rhs.mNothing = true;
216        reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
217        rhs.destroy();
218    } else if (mNothing) {
219        // We are nothing but rhs is something.
220        mNothing = false;
221        rhs.mNothing = true;
222
223        // Move the value from rhs.
224        new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
225        rhs.destroy();
226    } else {
227        // We are something but rhs is nothing, so destroy our value.
228        mNothing = true;
229        destroy();
230    }
231    return *this;
232}
233
234template <typename T>
235Maybe<T>::Maybe(const T& value)
236: mNothing(false) {
237    new (&mStorage) T(value);
238}
239
240template <typename T>
241Maybe<T>::Maybe(T&& value)
242: mNothing(false) {
243    new (&mStorage) T(std::forward<T>(value));
244}
245
246template <typename T>
247Maybe<T>::operator bool() const {
248    return !mNothing;
249}
250
251template <typename T>
252T& Maybe<T>::value() {
253    assert(!mNothing && "Maybe<T>::value() called on Nothing");
254    return reinterpret_cast<T&>(mStorage);
255}
256
257template <typename T>
258const T& Maybe<T>::value() const {
259    assert(!mNothing && "Maybe<T>::value() called on Nothing");
260    return reinterpret_cast<const T&>(mStorage);
261}
262
263template <typename T>
264void Maybe<T>::destroy() {
265    reinterpret_cast<T&>(mStorage).~T();
266}
267
268template <typename T>
269inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
270    return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
271}
272
273template <typename T>
274inline Maybe<T> make_nothing() {
275    return Maybe<T>();
276}
277
278} // namespace aapt
279
280#endif // AAPT_MAYBE_H
281