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>
This commit is contained in:
parent
d7f5bc80fb
commit
8f73edeaf6
@ -21,66 +21,123 @@ namespace skstd {
|
||||
template<typename T>
|
||||
class optional {
|
||||
public:
|
||||
optional(const optional&) = delete;
|
||||
optional(const T& value)
|
||||
: fHasValue(true) {
|
||||
new(&fPayload.fValue) T(value);
|
||||
}
|
||||
|
||||
optional(T&& value)
|
||||
: fHasValue(true) {
|
||||
new(&fPayload.fValue) T(std::move(value));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
optional(Args&&... args)
|
||||
: optional(T(std::forward<Args...>(args...))) {}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
~optional() {
|
||||
if (fHasValue) {
|
||||
fPayload.fValue.~T();
|
||||
}
|
||||
template<typename... Args>
|
||||
optional(Args&&... args) {
|
||||
fHasValue = true;
|
||||
new(&fPayload.fValue) T(std::forward<Args...>(args...));
|
||||
}
|
||||
|
||||
optional& operator=(const optional&) = delete;
|
||||
~optional() {
|
||||
this->reset();
|
||||
}
|
||||
|
||||
optional& operator=(optional&& other) {
|
||||
optional& operator=(const optional& other) {
|
||||
if (this != &other) {
|
||||
if (fHasValue) {
|
||||
fPayload.fValue.~T();
|
||||
}
|
||||
fHasValue = other.fHasValue;
|
||||
if (fHasValue) {
|
||||
new(&fPayload.fValue) T(std::move(other.fPayload.fValue));
|
||||
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;
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
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 &fPayload.fValue;
|
||||
return &this->value();
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
SkASSERT(fHasValue);
|
||||
return fPayload.fValue;
|
||||
return this->value();
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
SkASSERT(fHasValue);
|
||||
return &fPayload.fValue;
|
||||
return &this->value();
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return fHasValue;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return fHasValue;
|
||||
return this->has_value();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (fHasValue) {
|
||||
fPayload.fValue.~T();
|
||||
fHasValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -13,27 +13,137 @@
|
||||
DEF_TEST(SkTOptionalEmpty, r) {
|
||||
skstd::optional<int> o;
|
||||
REPORTER_ASSERT(r, !o);
|
||||
REPORTER_ASSERT(r, !o.has_value());
|
||||
}
|
||||
|
||||
DEF_TEST(SkTOptionalValue, r) {
|
||||
skstd::optional<const char*> o("test");
|
||||
REPORTER_ASSERT(r, o);
|
||||
REPORTER_ASSERT(r, o.has_value());
|
||||
REPORTER_ASSERT(r, !strcmp(*o, "test"));
|
||||
REPORTER_ASSERT(r, !strcmp(o.value(), "test"));
|
||||
o.reset();
|
||||
REPORTER_ASSERT(r, !o);
|
||||
REPORTER_ASSERT(r, !o.has_value());
|
||||
}
|
||||
|
||||
DEF_TEST(SkTOptionalAssignment, r) {
|
||||
skstd::optional<SkTArray<int>> o;
|
||||
class SkTOptionalTestPayload {
|
||||
public:
|
||||
enum State {
|
||||
kConstructed,
|
||||
kCopyConstructed,
|
||||
kCopyAssigned,
|
||||
kMoveConstructed,
|
||||
kMoveAssigned,
|
||||
kMovedFrom
|
||||
};
|
||||
|
||||
SkTOptionalTestPayload(int payload)
|
||||
: fState(kConstructed)
|
||||
, fPayload(payload) {}
|
||||
|
||||
SkTOptionalTestPayload(const SkTOptionalTestPayload& other)
|
||||
: fState(kCopyConstructed)
|
||||
, fPayload(other.fPayload) {}
|
||||
|
||||
SkTOptionalTestPayload(SkTOptionalTestPayload&& other)
|
||||
: fState(kMoveConstructed)
|
||||
, fPayload(other.fPayload) {
|
||||
other.fState = kMovedFrom;
|
||||
}
|
||||
|
||||
SkTOptionalTestPayload& operator=(const SkTOptionalTestPayload& other) {
|
||||
fState = kCopyAssigned;
|
||||
fPayload = other.fPayload;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SkTOptionalTestPayload& operator=(SkTOptionalTestPayload&& other) {
|
||||
fState = kMoveAssigned;
|
||||
fPayload = other.fPayload;
|
||||
other.fState = kMovedFrom;
|
||||
return *this;
|
||||
}
|
||||
State fState;
|
||||
int fPayload;
|
||||
};
|
||||
|
||||
DEF_TEST(SkTOptionalConstruction, r) {
|
||||
skstd::optional<SkTOptionalTestPayload> o(1);
|
||||
REPORTER_ASSERT(r, o);
|
||||
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kConstructed);
|
||||
REPORTER_ASSERT(r, o->fPayload == 1);
|
||||
|
||||
skstd::optional<SkTOptionalTestPayload> copy(o);
|
||||
REPORTER_ASSERT(r, copy);
|
||||
REPORTER_ASSERT(r, copy->fState == SkTOptionalTestPayload::kCopyConstructed);
|
||||
REPORTER_ASSERT(r, copy->fPayload == 1);
|
||||
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kConstructed);
|
||||
|
||||
skstd::optional<SkTOptionalTestPayload> move(std::move(o));
|
||||
REPORTER_ASSERT(r, move);
|
||||
REPORTER_ASSERT(r, move->fState == SkTOptionalTestPayload::kMoveConstructed);
|
||||
REPORTER_ASSERT(r, move->fPayload == 1);
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move)
|
||||
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kMovedFrom);
|
||||
}
|
||||
|
||||
DEF_TEST(SkTOptionalMoveAssignment, r) {
|
||||
skstd::optional<SkTOptionalTestPayload> o;
|
||||
REPORTER_ASSERT(r, !o);
|
||||
|
||||
o = skstd::optional<SkTArray<int>>(50);
|
||||
REPORTER_ASSERT(r, o);
|
||||
REPORTER_ASSERT(r, o->capacity() == 50);
|
||||
// assign to an empty optional from an empty optional
|
||||
o = skstd::optional<SkTOptionalTestPayload>();
|
||||
REPORTER_ASSERT(r, !o);
|
||||
|
||||
o = skstd::optional<SkTArray<int>>(SkTArray<int>(100));
|
||||
// assign to an empty optional from a full optional
|
||||
skstd::optional<SkTOptionalTestPayload> full(1);
|
||||
o = std::move(full);
|
||||
REPORTER_ASSERT(r, o);
|
||||
REPORTER_ASSERT(r, o->capacity() == 100);
|
||||
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kMoveConstructed);
|
||||
REPORTER_ASSERT(r, o->fPayload == 1);
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move)
|
||||
REPORTER_ASSERT(r, full->fState == SkTOptionalTestPayload::kMovedFrom);
|
||||
|
||||
o = skstd::optional<SkTArray<int>>();
|
||||
// assign to a full optional from a full optional
|
||||
full = skstd::optional<SkTOptionalTestPayload>(2);
|
||||
o = std::move(full);
|
||||
REPORTER_ASSERT(r, o);
|
||||
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kMoveAssigned);
|
||||
REPORTER_ASSERT(r, o->fPayload == 2);
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move)
|
||||
REPORTER_ASSERT(r, full->fState == SkTOptionalTestPayload::kMovedFrom);
|
||||
|
||||
// assign to a full optional from an empty optional
|
||||
o = skstd::optional<SkTOptionalTestPayload>();
|
||||
REPORTER_ASSERT(r, !o);
|
||||
}
|
||||
|
||||
DEF_TEST(SkTOptionalCopyAssignment, r) {
|
||||
skstd::optional<SkTOptionalTestPayload> o;
|
||||
REPORTER_ASSERT(r, !o);
|
||||
|
||||
skstd::optional<SkTOptionalTestPayload> empty;
|
||||
skstd::optional<SkTOptionalTestPayload> full(1);
|
||||
|
||||
// assign to an empty optional from an empty optional
|
||||
o = empty;
|
||||
REPORTER_ASSERT(r, !o);
|
||||
|
||||
// assign to an empty optional from a full optional
|
||||
o = full;
|
||||
REPORTER_ASSERT(r, o);
|
||||
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kCopyConstructed);
|
||||
REPORTER_ASSERT(r, o->fPayload == 1);
|
||||
|
||||
// assign to a full optional from a full optional
|
||||
o = full;
|
||||
REPORTER_ASSERT(r, o);
|
||||
REPORTER_ASSERT(r, o->fState == SkTOptionalTestPayload::kCopyAssigned);
|
||||
REPORTER_ASSERT(r, o->fPayload == 1);
|
||||
|
||||
// assign to a full optional from an empty optional
|
||||
o = empty;
|
||||
REPORTER_ASSERT(r, !o);
|
||||
}
|
||||
|
||||
@ -59,11 +169,16 @@ DEF_TEST(SkTOptionalNoDefaultConstructor, r) {
|
||||
DEF_TEST(SkTOptionalSelfAssignment, r) {
|
||||
skstd::optional<SkString> empty;
|
||||
skstd::optional<SkString>& emptyRef = empty;
|
||||
empty = emptyRef;
|
||||
REPORTER_ASSERT(r, !empty);
|
||||
empty = std::move(emptyRef);
|
||||
REPORTER_ASSERT(r, !empty);
|
||||
|
||||
skstd::optional<SkString> full("full");
|
||||
skstd::optional<SkString>& fullRef = full;
|
||||
full = fullRef;
|
||||
REPORTER_ASSERT(r, full);
|
||||
REPORTER_ASSERT(r, *full == SkString("full"));
|
||||
full = std::move(fullRef);
|
||||
REPORTER_ASSERT(r, full);
|
||||
REPORTER_ASSERT(r, *full == SkString("full"));
|
||||
|
Loading…
Reference in New Issue
Block a user