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