skia2/include/private/SkTOptional.h
John Stiles 82bbe606ec Add static to global nullopt symbol.
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>
2021-07-13 14:00:03 +00:00

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