1 // Copyright 2018 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef LIB_FIT_OPTIONAL_H_ 6 #define LIB_FIT_OPTIONAL_H_ 7 8 #include <assert.h> 9 10 #include <new> 11 #include <type_traits> 12 #include <utility> 13 14 namespace fit { 15 namespace internal { 16 17 template <typename T, bool = std::is_assignable<T&, const T&>::value> 18 struct copy_assign_or_reconstruct final { assignfinal19 static void assign(T* dest, const T& source) { 20 dest->~T(); 21 new (dest) T(source); 22 } 23 }; 24 25 template <typename T> 26 struct copy_assign_or_reconstruct<T, true> final { 27 static void assign(T* dest, const T& source) { 28 *dest = source; 29 } 30 }; 31 32 template <typename T, bool = std::is_assignable<T&, T&&>::value> 33 struct move_assign_or_reconstruct final { 34 static void assign(T* dest, T&& source) { 35 dest->~T(); 36 new (dest) T(std::move(source)); 37 } 38 39 static void swap(T& a, T& b) { 40 T temp(std::move(a)); 41 a.~T(); 42 new (&a) T(std::move(b)); 43 b.~T(); 44 new (&b) T(std::move(temp)); 45 } 46 }; 47 48 template <typename T> 49 struct move_assign_or_reconstruct<T, true> final { 50 static void assign(T* dest, T&& source) { 51 *dest = std::move(source); 52 } 53 54 static void swap(T& a, T& b) { 55 using std::swap; 56 swap(a, b); 57 } 58 }; 59 60 } // namespace internal 61 62 // A sentinel value for |fit::optional<T>| indicating that it contains 63 // no value. 64 struct nullopt_t { 65 explicit constexpr nullopt_t(int) {} 66 }; 67 static constexpr nullopt_t nullopt(0); 68 69 // A minimal implementation of an |std::optional<T>| work-alike for C++ 14. 70 // 71 // See also |fit::nullable<T>| which may be more efficient in certain 72 // circumstances if T can be initialized, assigned, and compared with 73 // nullptr. 74 // 75 // TODO(US-90): The initial implementation only covers a minimal subset of the 76 // std::optional API. Flesh this out more fully then define fit::optional 77 // to be an alias for std::optional when compiling with C++ 17. 78 template <typename T> 79 class optional final { 80 public: 81 using value_type = T; 82 83 constexpr optional() 84 : has_value_(false) {} 85 constexpr optional(nullopt_t) 86 : has_value_(false) {} 87 88 explicit constexpr optional(T value) 89 : has_value_(true), value_(std::move(value)) {} 90 91 optional(const optional& other) 92 : has_value_(other.has_value_) { 93 if (has_value_) { 94 new (&value_) T(other.value_); 95 } 96 } 97 98 optional(optional&& other) 99 : has_value_(other.has_value_) { 100 if (has_value_) { 101 new (&value_) T(std::move(other.value_)); 102 other.value_.~T(); 103 other.has_value_ = false; 104 } 105 } 106 107 // TODO(US-90): Presence of this destructor makes the type non-literal. 108 // We should specialize this type to handle the case where T is literal 109 // explicitly so that expressions these types can be constexpr. 110 ~optional() { 111 if (has_value_) { 112 value_.~T(); 113 } 114 } 115 116 constexpr T& value() & { 117 assert(has_value_); 118 return value_; 119 } 120 121 constexpr const T& value() const& { 122 assert(has_value_); 123 return value_; 124 } 125 126 constexpr T&& value() && { 127 assert(has_value_); 128 return std::move(value_); 129 } 130 131 constexpr const T&& value() const&& { 132 assert(has_value_); 133 return std::move(value_); 134 } 135 136 template <typename U = T> 137 constexpr T value_or(U&& default_value) const { 138 return has_value_ ? value_ : static_cast<T>(std::forward<U>(default_value)); 139 } 140 141 constexpr T* operator->() { return &value_; } 142 constexpr const T* operator->() const { return &value_; } 143 constexpr T& operator*() { return value_; } 144 constexpr const T& operator*() const { return value_; } 145 146 bool has_value() const { return has_value_; } 147 explicit operator bool() const { return has_value(); } 148 149 optional& operator=(const optional& other) { 150 if (&other == this) 151 return *this; 152 if (has_value_) { 153 if (other.has_value_) { 154 ::fit::internal::copy_assign_or_reconstruct<T>::assign( 155 &value_, other.value_); 156 } else { 157 reset(); 158 } 159 } else if (other.has_value_) { 160 new (&value_) T(other.value_); 161 has_value_ = true; 162 } 163 return *this; 164 } 165 166 optional& operator=(optional&& other) { 167 if (&other == this) 168 return *this; 169 if (has_value_) { 170 if (other.has_value_) { 171 ::fit::internal::move_assign_or_reconstruct<T>::assign( 172 &value_, std::move(other.value_)); 173 other.value_.~T(); 174 other.has_value_ = false; 175 } else { 176 reset(); 177 } 178 } else if (other.has_value_) { 179 new (&value_) T(std::move(other.value_)); 180 has_value_ = true; 181 other.value_.~T(); 182 other.has_value_ = false; 183 } 184 return *this; 185 } 186 187 optional& operator=(nullopt_t) { 188 reset(); 189 return *this; 190 } 191 192 optional& operator=(T value) { 193 if (has_value_) { 194 ::fit::internal::move_assign_or_reconstruct<T>::assign( 195 &value_, std::move(value)); 196 } else { 197 new (&value_) T(std::move(value)); 198 has_value_ = true; 199 } 200 return *this; 201 } 202 203 void reset() { 204 if (has_value_) { 205 value_.~T(); 206 has_value_ = false; 207 } 208 } 209 210 void swap(optional& other) { 211 if (&other == this) 212 return; 213 if (has_value_) { 214 if (other.has_value_) { 215 ::fit::internal::move_assign_or_reconstruct<T>::swap( 216 value_, other.value_); 217 } else { 218 new (&other.value_) T(std::move(value_)); 219 other.has_value_ = true; 220 value_.~T(); 221 has_value_ = false; 222 } 223 } else if (other.has_value_) { 224 new (&value_) T(std::move(other.value_)); 225 has_value_ = true; 226 other.value_.~T(); 227 other.has_value_ = false; 228 } 229 } 230 231 template <typename... Args> 232 T& emplace(Args&&... args) { 233 reset(); 234 new (&value_) T(std::forward<Args...>(args)...); 235 has_value_ = true; 236 return value_; 237 } 238 239 private: 240 bool has_value_; 241 union { 242 T value_; 243 }; 244 }; 245 246 template <typename T> 247 void swap(optional<T>& a, optional<T>& b) { 248 a.swap(b); 249 } 250 251 template <typename T> 252 constexpr bool operator==(const optional<T>& lhs, nullopt_t) { 253 return !lhs.has_value(); 254 } 255 template <typename T> 256 constexpr bool operator!=(const optional<T>& lhs, nullopt_t) { 257 return lhs.has_value(); 258 } 259 260 template <typename T> 261 constexpr bool operator==(nullopt_t, const optional<T>& rhs) { 262 return !rhs.has_value(); 263 } 264 template <typename T> 265 constexpr bool operator!=(nullopt_t, const optional<T>& rhs) { 266 return rhs.has_value(); 267 } 268 269 template <typename T, typename U> 270 constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) { 271 return (lhs.has_value() == rhs.has_value()) && (!lhs.has_value() || *lhs == *rhs); 272 } 273 template <typename T, typename U> 274 constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) { 275 return (lhs.has_value() != rhs.has_value()) || (lhs.has_value() && *lhs != *rhs); 276 } 277 278 template <typename T, typename U> 279 constexpr bool operator==(const optional<T>& lhs, const U& rhs) { 280 return lhs.has_value() && *lhs == rhs; 281 } 282 template <typename T, typename U> 283 constexpr bool operator!=(const optional<T>& lhs, const U& rhs) { 284 return !lhs.has_value() || *lhs != rhs; 285 } 286 287 template <typename T, typename U> 288 constexpr bool operator==(const T& lhs, const optional<U>& rhs) { 289 return rhs.has_value() && lhs == *rhs; 290 } 291 template <typename T, typename U> 292 constexpr bool operator!=(const T& lhs, const optional<U>& rhs) { 293 return !rhs.has_value() || lhs != *rhs; 294 } 295 296 } // namespace fit 297 298 #endif // LIB_FIT_OPTIONAL_H_ 299