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