/* * Copyright 2021 Google LLC. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTOptional_DEFINED #define SkTOptional_DEFINED #include "include/core/SkTypes.h" #include namespace skstd { /** * An empty optional is represented with `nullopt`. */ struct nullopt_t { struct tag {}; // nullopt_t must not be default-constructible. explicit constexpr nullopt_t(tag) {} }; static constexpr nullopt_t nullopt{nullopt_t::tag{}}; /** * Simple drop-in replacement for std::optional until we move to C++17. This does not have all of * std::optional's capabilities, but it covers our needs for the time being. */ template class optional { public: optional(const T& value) : fHasValue(true) { new(&fPayload.fValue) T(value); } optional(T&& value) : fHasValue(true) { new(&fPayload.fValue) T(std::move(value)); } optional() {} optional(const optional& other) { *this = other; } // Construction with nullopt is the same as default construction. optional(nullopt_t) : optional() {} // We need a non-const copy constructor because otherwise optional(nonConstSrc) isn't an exact // match for the copy constructor, and we'd end up invoking the Args&&... template by mistake. optional(optional& other) { *this = other; } optional(optional&& other) { *this = std::move(other); } template optional(Args&&... args) { fHasValue = true; new(&fPayload.fValue) T(std::forward(args...)); } ~optional() { this->reset(); } optional& operator=(const optional& other) { if (this != &other) { if (fHasValue) { if (other.fHasValue) { fPayload.fValue = other.fPayload.fValue; } else { this->reset(); } } else { if (other.fHasValue) { fHasValue = true; new (&fPayload.fValue) T(other.fPayload.fValue); } else { // do nothing, no value on either side } } } return *this; } optional& operator=(optional&& other) { if (this != &other) { if (fHasValue) { if (other.fHasValue) { fPayload.fValue = std::move(other.fPayload.fValue); } else { this->reset(); } } else { if (other.fHasValue) { fHasValue = true; new (&fPayload.fValue) T(std::move(other.fPayload.fValue)); } else { // do nothing, no value on either side } } } return *this; } // Assignment to nullopt is the same as reset(). optional& operator=(nullopt_t) { this->reset(); return *this; } T& operator*() & { SkASSERT(fHasValue); return fPayload.fValue; } const T& operator*() const& { SkASSERT(fHasValue); return fPayload.fValue; } T&& operator*() && { SkASSERT(fHasValue); return std::move(fPayload.fValue); } const T&& operator*() const&& { SkASSERT(fHasValue); return std::move(fPayload.fValue); } const T& value() const& { SkASSERT_RELEASE(fHasValue); return **this; } T& value() & { SkASSERT_RELEASE(fHasValue); return **this; } const T&& value() const&& { SkASSERT_RELEASE(fHasValue); return std::move(**this); } T&& value() && { SkASSERT_RELEASE(fHasValue); return std::move(**this); } T* operator->() { return &**this; } const T* operator->() const { return &**this; } template T value_or(U&& value) const& { return this->has_value() ? **this : static_cast(std::forward(value)); } template T value_or(U&& value) && { return this->has_value() ? std::move(**this) : static_cast(std::forward(value)); } bool has_value() const { return fHasValue; } explicit operator bool() const { return this->has_value(); } void reset() { if (fHasValue) { fPayload.fValue.~T(); fHasValue = false; } } private: union Payload { T fValue; Payload() {} ~Payload() {} } fPayload; bool fHasValue = false; }; // Comparison operators for optional x optional template bool operator==(const optional& a, const optional& b) { return (a.has_value() != b.has_value()) ? false : !a.has_value() ? true : (*a == *b); } template bool operator!=(const optional& a, const optional& b) { return (a.has_value() != b.has_value()) ? true : !a.has_value() ? false : (*a != *b); } template bool operator<(const optional& a, const optional& b) { return !b.has_value() ? false : !a.has_value() ? true : (*a < *b); } template bool operator<=(const optional& a, const optional& b) { return !a.has_value() ? true : !b.has_value() ? false : (*a <= *b); } template bool operator>(const optional& a, const optional& b) { return !a.has_value() ? false : !b.has_value() ? true : (*a > *b); } template bool operator>=(const optional& a, const optional& b) { return !b.has_value() ? true : !a.has_value() ? false : (*a >= *b); } // Comparison operators for optional x nullopt template bool operator==(const optional& a, nullopt_t) { return !a.has_value(); } template bool operator!=(const optional& a, nullopt_t) { return a.has_value(); } template bool operator<(const optional&, nullopt_t) { return false; } template bool operator<=(const optional& a, nullopt_t) { return !a.has_value(); } template bool operator>(const optional& a, nullopt_t) { return a.has_value(); } template bool operator>=(const optional&, nullopt_t) { return true; } // Comparison operators for nullopt x optional template bool operator==(nullopt_t, const optional& b) { return !b.has_value(); } template bool operator!=(nullopt_t, const optional& b) { return b.has_value(); } template bool operator<(nullopt_t, const optional& b) { return b.has_value(); } template bool operator<=(nullopt_t, const optional&) { return true; } template bool operator>(nullopt_t, const optional&) { return false; } template bool operator>=(nullopt_t, const optional& b) { return !b.has_value(); } // Comparison operators for optional x value template bool operator==(const optional& a, const U& b) { return a.has_value() && (*a == b); } template bool operator!=(const optional& a, const U& b) { return !a.has_value() || (*a != b); } template bool operator<(const optional& a, const U& b) { return !a.has_value() || (*a < b); } template bool operator<=(const optional& a, const U& b) { return !a.has_value() || (*a <= b); } template bool operator>(const optional& a, const U& b) { return a.has_value() && (*a > b); } template bool operator>=(const optional& a, const U& b) { return a.has_value() && (*a >= b); } // Comparison operators for value x optional template bool operator==(const T& a, const optional& b) { return b.has_value() && (a == *b); } template bool operator!=(const T& a, const optional& b) { return !b.has_value() || (a != *b); } template bool operator<(const T& a, const optional& b) { return b.has_value() && (a < *b); } template bool operator<=(const T& a, const optional& b) { return b.has_value() && (a <= *b); } template bool operator>(const T& a, const optional& b) { return !b.has_value() || (a > *b); } template bool operator>=(const T& a, const optional& b) { return !b.has_value() || (a >= *b); } } // namespace skstd #endif