skia2/include/private/SkTOptional.h
Ethan Nicholas 8f73edeaf6 Added more SkTOptional APIs
This adds copy constructors / assignment operator, reset(), value(), and
has_value() to SkTOptional.

Change-Id: I564552a75a4c612685cdaa8e80e4359b394b64a4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/414338
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2021-06-01 18:54:13 +00:00

158 lines
3.4 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 {
/**
* 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;
}
// 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;
}
const T& value() const {
SkASSERT(fHasValue);
return fPayload.fValue;
}
T& value() {
return fPayload.fValue;
}
T& operator*() {
SkASSERT(fHasValue);
return this->value();
}
T* operator->() {
SkASSERT(fHasValue);
return &this->value();
}
const T& operator*() const {
return this->value();
}
const T* operator->() const {
SkASSERT(fHasValue);
return &this->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;
};
} // namespace skstd
#endif