82bbe606ec
This should hopefully make the code safer in C++14 by not violating the ODR. (bungeman@: "I think in C++14 we will in fact get multiple definitions of this symbol, though in C++17 we won't. Maybe we should mark this as 'static' so each TU gets its own zero byte copy in C++14 but in C++17 the 'static' will also be implicitly 'static inline' and we'll only get one?) Change-Id: I3570421fcf2be314e1baccff0783ded4a1078666 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/427037 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: John Stiles <johnstiles@google.com>
347 lines
8.8 KiB
C++
347 lines
8.8 KiB
C++
/*
|
|
* 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 <utility>
|
|
|
|
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<typename T>
|
|
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<typename... Args>
|
|
optional(Args&&... args) {
|
|
fHasValue = true;
|
|
new(&fPayload.fValue) T(std::forward<Args...>(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<typename U>
|
|
T value_or(U&& value) const& {
|
|
return this->has_value() ? **this : static_cast<T>(std::forward<U>(value));
|
|
}
|
|
|
|
template<typename U>
|
|
T value_or(U&& value) && {
|
|
return this->has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(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 <typename T, typename U> bool operator==(const optional<T>& a, const optional<U>& b) {
|
|
return (a.has_value() != b.has_value()) ? false :
|
|
!a.has_value() ? true :
|
|
(*a == *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator!=(const optional<T>& a, const optional<U>& b) {
|
|
return (a.has_value() != b.has_value()) ? true :
|
|
!a.has_value() ? false :
|
|
(*a != *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator<(const optional<T>& a, const optional<U>& b) {
|
|
return !b.has_value() ? false :
|
|
!a.has_value() ? true :
|
|
(*a < *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator<=(const optional<T>& a, const optional<U>& b) {
|
|
return !a.has_value() ? true :
|
|
!b.has_value() ? false :
|
|
(*a <= *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator>(const optional<T>& a, const optional<U>& b) {
|
|
return !a.has_value() ? false :
|
|
!b.has_value() ? true :
|
|
(*a > *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator>=(const optional<T>& a, const optional<U>& b) {
|
|
return !b.has_value() ? true :
|
|
!a.has_value() ? false :
|
|
(*a >= *b);
|
|
}
|
|
|
|
// Comparison operators for optional x nullopt
|
|
template <typename T> bool operator==(const optional<T>& a, nullopt_t) {
|
|
return !a.has_value();
|
|
}
|
|
|
|
template <typename T> bool operator!=(const optional<T>& a, nullopt_t) {
|
|
return a.has_value();
|
|
}
|
|
|
|
template <typename T> bool operator<(const optional<T>&, nullopt_t) {
|
|
return false;
|
|
}
|
|
|
|
template <typename T> bool operator<=(const optional<T>& a, nullopt_t) {
|
|
return !a.has_value();
|
|
}
|
|
|
|
template <typename T> bool operator>(const optional<T>& a, nullopt_t) {
|
|
return a.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator>=(const optional<T>&, nullopt_t) {
|
|
return true;
|
|
}
|
|
|
|
// Comparison operators for nullopt x optional
|
|
template <typename U> bool operator==(nullopt_t, const optional<U>& b) {
|
|
return !b.has_value();
|
|
}
|
|
|
|
template <typename U> bool operator!=(nullopt_t, const optional<U>& b) {
|
|
return b.has_value();
|
|
}
|
|
|
|
template <typename U> bool operator<(nullopt_t, const optional<U>& b) {
|
|
return b.has_value();
|
|
}
|
|
|
|
template <typename U> bool operator<=(nullopt_t, const optional<U>&) {
|
|
return true;
|
|
}
|
|
|
|
template <typename U> bool operator>(nullopt_t, const optional<U>&) {
|
|
return false;
|
|
}
|
|
|
|
template <typename U> bool operator>=(nullopt_t, const optional<U>& b) {
|
|
return !b.has_value();
|
|
}
|
|
|
|
// Comparison operators for optional x value
|
|
template <typename T, typename U> bool operator==(const optional<T>& a, const U& b) {
|
|
return a.has_value() && (*a == b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator!=(const optional<T>& a, const U& b) {
|
|
return !a.has_value() || (*a != b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator<(const optional<T>& a, const U& b) {
|
|
return !a.has_value() || (*a < b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator<=(const optional<T>& a, const U& b) {
|
|
return !a.has_value() || (*a <= b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator>(const optional<T>& a, const U& b) {
|
|
return a.has_value() && (*a > b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator>=(const optional<T>& a, const U& b) {
|
|
return a.has_value() && (*a >= b);
|
|
}
|
|
|
|
// Comparison operators for value x optional
|
|
template <typename T, typename U> bool operator==(const T& a, const optional<U>& b) {
|
|
return b.has_value() && (a == *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator!=(const T& a, const optional<U>& b) {
|
|
return !b.has_value() || (a != *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator<(const T& a, const optional<U>& b) {
|
|
return b.has_value() && (a < *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator<=(const T& a, const optional<U>& b) {
|
|
return b.has_value() && (a <= *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator>(const T& a, const optional<U>& b) {
|
|
return !b.has_value() || (a > *b);
|
|
}
|
|
|
|
template <typename T, typename U> bool operator>=(const T& a, const optional<U>& b) {
|
|
return !b.has_value() || (a >= *b);
|
|
}
|
|
|
|
} // namespace skstd
|
|
|
|
#endif
|