Home | History | Annotate | Download | only in util
      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 "util/TypeTraits.h"
     21 
     22 #include <cassert>
     23 #include <type_traits>
     24 #include <utility>
     25 
     26 namespace 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  */
     33 template <typename T>
     34 class Maybe {
     35 public:
     36     /**
     37      * Construct Nothing.
     38      */
     39     Maybe();
     40 
     41     ~Maybe();
     42 
     43     Maybe(const Maybe& rhs);
     44 
     45     template <typename U>
     46     Maybe(const Maybe<U>& rhs);
     47 
     48     Maybe(Maybe&& rhs);
     49 
     50     template <typename U>
     51     Maybe(Maybe<U>&& rhs);
     52 
     53     Maybe& operator=(const Maybe& rhs);
     54 
     55     template <typename U>
     56     Maybe& operator=(const Maybe<U>& rhs);
     57 
     58     Maybe& operator=(Maybe&& rhs);
     59 
     60     template <typename U>
     61     Maybe& operator=(Maybe<U>&& rhs);
     62 
     63     /**
     64      * Construct a Maybe holding a value.
     65      */
     66     Maybe(const T& value);
     67 
     68     /**
     69      * Construct a Maybe holding a value.
     70      */
     71     Maybe(T&& value);
     72 
     73     /**
     74      * True if this holds a value, false if
     75      * it holds Nothing.
     76      */
     77     explicit operator bool() const;
     78 
     79     /**
     80      * Gets the value if one exists, or else
     81      * panics.
     82      */
     83     T& value();
     84 
     85     /**
     86      * Gets the value if one exists, or else
     87      * panics.
     88      */
     89     const T& value() const;
     90 
     91 private:
     92     template <typename U>
     93     friend class Maybe;
     94 
     95     template <typename U>
     96     Maybe& copy(const Maybe<U>& rhs);
     97 
     98     template <typename U>
     99     Maybe& move(Maybe<U>&& rhs);
    100 
    101     void destroy();
    102 
    103     bool mNothing;
    104 
    105     typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
    106 };
    107 
    108 template <typename T>
    109 Maybe<T>::Maybe()
    110 : mNothing(true) {
    111 }
    112 
    113 template <typename T>
    114 Maybe<T>::~Maybe() {
    115     if (!mNothing) {
    116         destroy();
    117     }
    118 }
    119 
    120 template <typename T>
    121 Maybe<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 
    128 template <typename T>
    129 template <typename U>
    130 Maybe<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 
    137 template <typename T>
    138 Maybe<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 
    149 template <typename T>
    150 template <typename U>
    151 Maybe<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)));
    158         rhs.destroy();
    159     }
    160 }
    161 
    162 template <typename T>
    163 inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
    164     // Delegate to the actual assignment.
    165     return copy(rhs);
    166 }
    167 
    168 template <typename T>
    169 template <typename U>
    170 inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
    171     return copy(rhs);
    172 }
    173 
    174 template <typename T>
    175 template <typename U>
    176 Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
    177     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 
    197 template <typename T>
    198 inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
    199     // Delegate to the actual assignment.
    200     return move(std::forward<Maybe<T>>(rhs));
    201 }
    202 
    203 template <typename T>
    204 template <typename U>
    205 inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
    206     return move(std::forward<Maybe<U>>(rhs));
    207 }
    208 
    209 template <typename T>
    210 template <typename U>
    211 Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
    212     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.
    222         mNothing = false;
    223         rhs.mNothing = true;
    224 
    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.
    230         mNothing = true;
    231         destroy();
    232     }
    233     return *this;
    234 }
    235 
    236 template <typename T>
    237 Maybe<T>::Maybe(const T& value)
    238 : mNothing(false) {
    239     new (&mStorage) T(value);
    240 }
    241 
    242 template <typename T>
    243 Maybe<T>::Maybe(T&& value)
    244 : mNothing(false) {
    245     new (&mStorage) T(std::forward<T>(value));
    246 }
    247 
    248 template <typename T>
    249 Maybe<T>::operator bool() const {
    250     return !mNothing;
    251 }
    252 
    253 template <typename T>
    254 T& Maybe<T>::value() {
    255     assert(!mNothing && "Maybe<T>::value() called on Nothing");
    256     return reinterpret_cast<T&>(mStorage);
    257 }
    258 
    259 template <typename T>
    260 const T& Maybe<T>::value() const {
    261     assert(!mNothing && "Maybe<T>::value() called on Nothing");
    262     return reinterpret_cast<const T&>(mStorage);
    263 }
    264 
    265 template <typename T>
    266 void Maybe<T>::destroy() {
    267     reinterpret_cast<T&>(mStorage).~T();
    268 }
    269 
    270 template <typename T>
    271 inline 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 
    275 template <typename T>
    276 inline Maybe<T> make_nothing() {
    277     return Maybe<T>();
    278 }
    279 
    280 /**
    281  * 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.
    284  */
    285 template <typename T, typename U>
    286 typename std::enable_if<
    287         has_eq_op<T, U>::value,
    288         bool
    289 >::type operator==(const Maybe<T>& a, 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 /**
    299  * Same as operator== but negated.
    300  */
    301 template <typename T, typename U>
    302 typename std::enable_if<
    303         has_eq_op<T, U>::value,
    304         bool
    305 >::type operator!=(const Maybe<T>& a, const Maybe<U>& b) {
    306     return !(a == b);
    307 }
    308 
    309 } // namespace aapt
    310 
    311 #endif // AAPT_MAYBE_H
    312