1 // Copyright 2016 The Chromium 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 BASE_OPTIONAL_H_ 6 #define BASE_OPTIONAL_H_ 7 8 #include <type_traits> 9 10 #include "base/logging.h" 11 #include "base/memory/aligned_memory.h" 12 #include "base/template_util.h" 13 14 namespace base { 15 16 // Specification: 17 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t 18 struct in_place_t {}; 19 20 // Specification: 21 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t 22 struct nullopt_t { 23 constexpr explicit nullopt_t(int) {} 24 }; 25 26 // Specification: 27 // http://en.cppreference.com/w/cpp/utility/optional/in_place 28 constexpr in_place_t in_place = {}; 29 30 // Specification: 31 // http://en.cppreference.com/w/cpp/utility/optional/nullopt 32 constexpr nullopt_t nullopt(0); 33 34 namespace internal { 35 36 template <typename T, bool = base::is_trivially_destructible<T>::value> 37 struct OptionalStorage { 38 // When T is not trivially destructible we must call its 39 // destructor before deallocating its memory. 40 ~OptionalStorage() { 41 if (!is_null_) 42 buffer_.template data_as<T>()->~T(); 43 } 44 45 bool is_null_ = true; 46 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; 47 }; 48 49 template <typename T> 50 struct OptionalStorage<T, true> { 51 // When T is trivially destructible (i.e. its destructor does nothing) 52 // there is no need to call it. 53 // Since |base::AlignedMemory| is just an array its destructor 54 // is trivial. Explicitly defaulting the destructor means it's not 55 // user-provided. All of this together make this destructor trivial. 56 ~OptionalStorage() = default; 57 58 bool is_null_ = true; 59 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; 60 }; 61 62 } // namespace internal 63 64 // base::Optional is a Chromium version of the C++17 optional class: 65 // std::optional documentation: 66 // http://en.cppreference.com/w/cpp/utility/optional 67 // Chromium documentation: 68 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md 69 // 70 // These are the differences between the specification and the implementation: 71 // - The constructor and emplace method using initializer_list are not 72 // implemented because 'initializer_list' is banned from Chromium. 73 // - Constructors do not use 'constexpr' as it is a C++14 extension. 74 // - 'constexpr' might be missing in some places for reasons specified locally. 75 // - No exceptions are thrown, because they are banned from Chromium. 76 // - All the non-members are in the 'base' namespace instead of 'std'. 77 template <typename T> 78 class Optional { 79 public: 80 using value_type = T; 81 82 constexpr Optional() = default; 83 Optional(base::nullopt_t) : Optional() {} 84 85 Optional(const Optional& other) { 86 if (!other.storage_.is_null_) 87 Init(other.value()); 88 } 89 90 Optional(Optional&& other) { 91 if (!other.storage_.is_null_) 92 Init(std::move(other.value())); 93 } 94 95 Optional(const T& value) { Init(value); } 96 97 Optional(T&& value) { Init(std::move(value)); } 98 99 template <class... Args> 100 explicit Optional(base::in_place_t, Args&&... args) { 101 emplace(std::forward<Args>(args)...); 102 } 103 104 ~Optional() = default; 105 106 Optional& operator=(base::nullopt_t) { 107 FreeIfNeeded(); 108 return *this; 109 } 110 111 Optional& operator=(const Optional& other) { 112 if (other.storage_.is_null_) { 113 FreeIfNeeded(); 114 return *this; 115 } 116 117 InitOrAssign(other.value()); 118 return *this; 119 } 120 121 Optional& operator=(Optional&& other) { 122 if (other.storage_.is_null_) { 123 FreeIfNeeded(); 124 return *this; 125 } 126 127 InitOrAssign(std::move(other.value())); 128 return *this; 129 } 130 131 template <class U> 132 typename std::enable_if<std::is_same<std::decay<U>, T>::value, 133 Optional&>::type 134 operator=(U&& value) { 135 InitOrAssign(std::forward<U>(value)); 136 return *this; 137 } 138 139 // TODO(mlamouri): can't use 'constexpr' with DCHECK. 140 const T* operator->() const { 141 DCHECK(!storage_.is_null_); 142 return &value(); 143 } 144 145 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was 146 // meant to be 'constexpr const'. 147 T* operator->() { 148 DCHECK(!storage_.is_null_); 149 return &value(); 150 } 151 152 constexpr const T& operator*() const& { return value(); } 153 154 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was 155 // meant to be 'constexpr const'. 156 T& operator*() & { return value(); } 157 158 constexpr const T&& operator*() const&& { return std::move(value()); } 159 160 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was 161 // meant to be 'constexpr const'. 162 T&& operator*() && { return std::move(value()); } 163 164 constexpr explicit operator bool() const { return !storage_.is_null_; } 165 166 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was 167 // meant to be 'constexpr const'. 168 T& value() & { 169 DCHECK(!storage_.is_null_); 170 return *storage_.buffer_.template data_as<T>(); 171 } 172 173 // TODO(mlamouri): can't use 'constexpr' with DCHECK. 174 const T& value() const& { 175 DCHECK(!storage_.is_null_); 176 return *storage_.buffer_.template data_as<T>(); 177 } 178 179 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was 180 // meant to be 'constexpr const'. 181 T&& value() && { 182 DCHECK(!storage_.is_null_); 183 return std::move(*storage_.buffer_.template data_as<T>()); 184 } 185 186 // TODO(mlamouri): can't use 'constexpr' with DCHECK. 187 const T&& value() const&& { 188 DCHECK(!storage_.is_null_); 189 return std::move(*storage_.buffer_.template data_as<T>()); 190 } 191 192 template <class U> 193 constexpr T value_or(U&& default_value) const& { 194 // TODO(mlamouri): add the following assert when possible: 195 // static_assert(std::is_copy_constructible<T>::value, 196 // "T must be copy constructible"); 197 static_assert(std::is_convertible<U, T>::value, 198 "U must be convertible to T"); 199 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) 200 : value(); 201 } 202 203 template <class U> 204 T value_or(U&& default_value) && { 205 // TODO(mlamouri): add the following assert when possible: 206 // static_assert(std::is_move_constructible<T>::value, 207 // "T must be move constructible"); 208 static_assert(std::is_convertible<U, T>::value, 209 "U must be convertible to T"); 210 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) 211 : std::move(value()); 212 } 213 214 void swap(Optional& other) { 215 if (storage_.is_null_ && other.storage_.is_null_) 216 return; 217 218 if (storage_.is_null_ != other.storage_.is_null_) { 219 if (storage_.is_null_) { 220 Init(std::move(*other.storage_.buffer_.template data_as<T>())); 221 other.FreeIfNeeded(); 222 } else { 223 other.Init(std::move(*storage_.buffer_.template data_as<T>())); 224 FreeIfNeeded(); 225 } 226 return; 227 } 228 229 DCHECK(!storage_.is_null_ && !other.storage_.is_null_); 230 using std::swap; 231 swap(**this, *other); 232 } 233 234 template <class... Args> 235 void emplace(Args&&... args) { 236 FreeIfNeeded(); 237 Init(std::forward<Args>(args)...); 238 } 239 240 private: 241 void Init(const T& value) { 242 DCHECK(storage_.is_null_); 243 new (storage_.buffer_.void_data()) T(value); 244 storage_.is_null_ = false; 245 } 246 247 void Init(T&& value) { 248 DCHECK(storage_.is_null_); 249 new (storage_.buffer_.void_data()) T(std::move(value)); 250 storage_.is_null_ = false; 251 } 252 253 template <class... Args> 254 void Init(Args&&... args) { 255 DCHECK(storage_.is_null_); 256 new (storage_.buffer_.void_data()) T(std::forward<Args>(args)...); 257 storage_.is_null_ = false; 258 } 259 260 void InitOrAssign(const T& value) { 261 if (storage_.is_null_) 262 Init(value); 263 else 264 *storage_.buffer_.template data_as<T>() = value; 265 } 266 267 void InitOrAssign(T&& value) { 268 if (storage_.is_null_) 269 Init(std::move(value)); 270 else 271 *storage_.buffer_.template data_as<T>() = std::move(value); 272 } 273 274 void FreeIfNeeded() { 275 if (storage_.is_null_) 276 return; 277 storage_.buffer_.template data_as<T>()->~T(); 278 storage_.is_null_ = true; 279 } 280 281 internal::OptionalStorage<T> storage_; 282 }; 283 284 template <class T> 285 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { 286 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs); 287 } 288 289 template <class T> 290 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { 291 return !(lhs == rhs); 292 } 293 294 template <class T> 295 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) { 296 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs); 297 } 298 299 template <class T> 300 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) { 301 return !(rhs < lhs); 302 } 303 304 template <class T> 305 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) { 306 return rhs < lhs; 307 } 308 309 template <class T> 310 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) { 311 return !(lhs < rhs); 312 } 313 314 template <class T> 315 constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) { 316 return !opt; 317 } 318 319 template <class T> 320 constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) { 321 return !opt; 322 } 323 324 template <class T> 325 constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) { 326 return !!opt; 327 } 328 329 template <class T> 330 constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) { 331 return !!opt; 332 } 333 334 template <class T> 335 constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) { 336 return false; 337 } 338 339 template <class T> 340 constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) { 341 return !!opt; 342 } 343 344 template <class T> 345 constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) { 346 return !opt; 347 } 348 349 template <class T> 350 constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) { 351 return true; 352 } 353 354 template <class T> 355 constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) { 356 return !!opt; 357 } 358 359 template <class T> 360 constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) { 361 return false; 362 } 363 364 template <class T> 365 constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) { 366 return true; 367 } 368 369 template <class T> 370 constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) { 371 return !opt; 372 } 373 374 template <class T> 375 constexpr bool operator==(const Optional<T>& opt, const T& value) { 376 return opt != nullopt ? *opt == value : false; 377 } 378 379 template <class T> 380 constexpr bool operator==(const T& value, const Optional<T>& opt) { 381 return opt == value; 382 } 383 384 template <class T> 385 constexpr bool operator!=(const Optional<T>& opt, const T& value) { 386 return !(opt == value); 387 } 388 389 template <class T> 390 constexpr bool operator!=(const T& value, const Optional<T>& opt) { 391 return !(opt == value); 392 } 393 394 template <class T> 395 constexpr bool operator<(const Optional<T>& opt, const T& value) { 396 return opt != nullopt ? *opt < value : true; 397 } 398 399 template <class T> 400 constexpr bool operator<(const T& value, const Optional<T>& opt) { 401 return opt != nullopt ? value < *opt : false; 402 } 403 404 template <class T> 405 constexpr bool operator<=(const Optional<T>& opt, const T& value) { 406 return !(opt > value); 407 } 408 409 template <class T> 410 constexpr bool operator<=(const T& value, const Optional<T>& opt) { 411 return !(value > opt); 412 } 413 414 template <class T> 415 constexpr bool operator>(const Optional<T>& opt, const T& value) { 416 return value < opt; 417 } 418 419 template <class T> 420 constexpr bool operator>(const T& value, const Optional<T>& opt) { 421 return opt < value; 422 } 423 424 template <class T> 425 constexpr bool operator>=(const Optional<T>& opt, const T& value) { 426 return !(opt < value); 427 } 428 429 template <class T> 430 constexpr bool operator>=(const T& value, const Optional<T>& opt) { 431 return !(value < opt); 432 } 433 434 template <class T> 435 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) { 436 return Optional<typename std::decay<T>::type>(std::forward<T>(value)); 437 } 438 439 template <class T> 440 void swap(Optional<T>& lhs, Optional<T>& rhs) { 441 lhs.swap(rhs); 442 } 443 444 } // namespace base 445 446 namespace std { 447 448 template <class T> 449 struct hash<base::Optional<T>> { 450 size_t operator()(const base::Optional<T>& opt) const { 451 return opt == base::nullopt ? 0 : std::hash<T>()(*opt); 452 } 453 }; 454 455 } // namespace std 456 457 #endif // BASE_OPTIONAL_H_ 458