Allow Ref<Derived> -> Ref<Base> assignment, and move operations
Previous to this change, you were unable to assign or move Ref<Derived> to a Ref<Base>. This change addresses the problem by introducing <typename U> versions of assignment, copy and move methods. nullptr_t specific ones were also added to disambiguate things for the compiler. Bug:dawn:390 Change-Id: Ib5d44231e26db35de33d63c67b36b5bf411a3540 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/20121 Commit-Queue: Rafael Cintron <rafael.cintron@microsoft.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
d4302437c9
commit
b4eccc8227
|
@ -41,16 +41,32 @@ class RefCounted {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Ref {
|
class Ref {
|
||||||
public:
|
public:
|
||||||
Ref() {
|
Ref() = default;
|
||||||
|
|
||||||
|
constexpr Ref(std::nullptr_t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref(T* p) : mPointee(p) {
|
Ref& operator=(std::nullptr_t) {
|
||||||
|
Release();
|
||||||
|
mPointee = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
Ref(U* p) : mPointee(p) {
|
||||||
|
static_assert(std::is_convertible<U*, T*>::value, "");
|
||||||
Reference();
|
Reference();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref(const Ref<T>& other) : mPointee(other.mPointee) {
|
Ref(const Ref<T>& other) : mPointee(other.mPointee) {
|
||||||
Reference();
|
Reference();
|
||||||
}
|
}
|
||||||
|
template <typename U>
|
||||||
|
Ref(const Ref<U>& other) : mPointee(other.mPointee) {
|
||||||
|
static_assert(std::is_convertible<U*, T*>::value, "");
|
||||||
|
Reference();
|
||||||
|
}
|
||||||
|
|
||||||
Ref<T>& operator=(const Ref<T>& other) {
|
Ref<T>& operator=(const Ref<T>& other) {
|
||||||
if (&other == this)
|
if (&other == this)
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -62,10 +78,24 @@ class Ref {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref(Ref<T>&& other) {
|
template <typename U>
|
||||||
|
Ref<T>& operator=(const Ref<U>& other) {
|
||||||
|
static_assert(std::is_convertible<U*, T*>::value, "");
|
||||||
|
|
||||||
|
other.Reference();
|
||||||
|
Release();
|
||||||
|
mPointee = other.mPointee;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
Ref(Ref<U>&& other) {
|
||||||
|
static_assert(std::is_convertible<U*, T*>::value, "");
|
||||||
mPointee = other.mPointee;
|
mPointee = other.mPointee;
|
||||||
other.mPointee = nullptr;
|
other.mPointee = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<T>& operator=(Ref<T>&& other) {
|
Ref<T>& operator=(Ref<T>&& other) {
|
||||||
if (&other == this)
|
if (&other == this)
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -76,6 +106,16 @@ class Ref {
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
template <typename U>
|
||||||
|
Ref<T>& operator=(Ref<U>&& other) {
|
||||||
|
static_assert(std::is_convertible<U*, T*>::value, "");
|
||||||
|
|
||||||
|
Release();
|
||||||
|
mPointee = other.mPointee;
|
||||||
|
other.mPointee = nullptr;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
~Ref() {
|
~Ref() {
|
||||||
Release();
|
Release();
|
||||||
|
@ -114,6 +154,11 @@ class Ref {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Friend is needed so that instances of Ref<U> can assign mPointee
|
||||||
|
// members of Ref<T>.
|
||||||
|
template <typename U>
|
||||||
|
friend class Ref;
|
||||||
|
|
||||||
void Reference() const {
|
void Reference() const {
|
||||||
if (mPointee != nullptr) {
|
if (mPointee != nullptr) {
|
||||||
mPointee->Reference();
|
mPointee->Reference();
|
||||||
|
@ -125,7 +170,6 @@ class Ref {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// static_assert(std::is_base_of<RefCounted, T>::value, "");
|
|
||||||
T* mPointee = nullptr;
|
T* mPointee = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,19 +17,20 @@
|
||||||
|
|
||||||
#include "common/RefCounted.h"
|
#include "common/RefCounted.h"
|
||||||
|
|
||||||
struct RCTest : public RefCounted {
|
class RCTest : public RefCounted {
|
||||||
|
public:
|
||||||
RCTest() : RefCounted() {
|
RCTest() : RefCounted() {
|
||||||
}
|
}
|
||||||
|
|
||||||
RCTest(uint64_t payload) : RefCounted(payload) {
|
RCTest(uint64_t payload) : RefCounted(payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
RCTest(bool* deleted) : deleted(deleted) {
|
RCTest(bool* deleted) : mDeleted(deleted) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~RCTest() override {
|
~RCTest() override {
|
||||||
if (deleted != nullptr) {
|
if (mDeleted != nullptr) {
|
||||||
*deleted = true;
|
*mDeleted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +38,19 @@ struct RCTest : public RefCounted {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool* deleted = nullptr;
|
private:
|
||||||
|
bool* mDeleted = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RCTestDerived : public RCTest {
|
||||||
|
RCTestDerived() : RCTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTestDerived(uint64_t payload) : RCTest(payload) {
|
||||||
|
}
|
||||||
|
|
||||||
|
RCTestDerived(bool* deleted) : RCTest(deleted) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test that RCs start with one ref, and removing it destroys the object.
|
// Test that RCs start with one ref, and removing it destroys the object.
|
||||||
|
@ -142,14 +155,21 @@ TEST(Ref, CopyConstructor) {
|
||||||
RCTest* original = new RCTest(&deleted);
|
RCTest* original = new RCTest(&deleted);
|
||||||
|
|
||||||
Ref<RCTest> source(original);
|
Ref<RCTest> source(original);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
Ref<RCTest> destination(source);
|
Ref<RCTest> destination(source);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 3u);
|
||||||
|
|
||||||
original->Release();
|
original->Release();
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
EXPECT_EQ(source.Get(), original);
|
EXPECT_EQ(source.Get(), original);
|
||||||
EXPECT_EQ(destination.Get(), original);
|
EXPECT_EQ(destination.Get(), original);
|
||||||
|
|
||||||
source = nullptr;
|
source = nullptr;
|
||||||
EXPECT_FALSE(deleted);
|
EXPECT_FALSE(deleted);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
destination = nullptr;
|
destination = nullptr;
|
||||||
EXPECT_TRUE(deleted);
|
EXPECT_TRUE(deleted);
|
||||||
}
|
}
|
||||||
|
@ -182,8 +202,13 @@ TEST(Ref, MoveConstructor) {
|
||||||
RCTest* original = new RCTest(&deleted);
|
RCTest* original = new RCTest(&deleted);
|
||||||
|
|
||||||
Ref<RCTest> source(original);
|
Ref<RCTest> source(original);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
Ref<RCTest> destination(std::move(source));
|
Ref<RCTest> destination(std::move(source));
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
original->Release();
|
original->Release();
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
EXPECT_EQ(source.Get(), nullptr);
|
EXPECT_EQ(source.Get(), nullptr);
|
||||||
EXPECT_EQ(destination.Get(), original);
|
EXPECT_EQ(destination.Get(), original);
|
||||||
|
@ -199,10 +224,14 @@ TEST(Ref, MoveAssignment) {
|
||||||
RCTest* original = new RCTest(&deleted);
|
RCTest* original = new RCTest(&deleted);
|
||||||
|
|
||||||
Ref<RCTest> source(original);
|
Ref<RCTest> source(original);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
original->Release();
|
original->Release();
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
Ref<RCTest> destination;
|
Ref<RCTest> destination;
|
||||||
destination = std::move(source);
|
destination = std::move(source);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
EXPECT_EQ(source.Get(), nullptr);
|
EXPECT_EQ(source.Get(), nullptr);
|
||||||
EXPECT_EQ(destination.Get(), original);
|
EXPECT_EQ(destination.Get(), original);
|
||||||
|
@ -212,6 +241,31 @@ TEST(Ref, MoveAssignment) {
|
||||||
EXPECT_TRUE(deleted);
|
EXPECT_TRUE(deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test move assigment where the destination and source
|
||||||
|
// point to the same underlying object.
|
||||||
|
TEST(Ref, MoveAssignmentSameObject) {
|
||||||
|
bool deleted = false;
|
||||||
|
RCTest* original = new RCTest(&deleted);
|
||||||
|
|
||||||
|
Ref<RCTest> source(original);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
|
original->Release();
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
|
Ref<RCTest>& referenceToSource = source;
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
|
referenceToSource = std::move(source);
|
||||||
|
|
||||||
|
EXPECT_EQ(source.Get(), original);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
EXPECT_FALSE(deleted);
|
||||||
|
|
||||||
|
source = nullptr;
|
||||||
|
EXPECT_TRUE(deleted);
|
||||||
|
}
|
||||||
|
|
||||||
// Test the payload initial value is set correctly
|
// Test the payload initial value is set correctly
|
||||||
TEST(Ref, InitialPayloadValue) {
|
TEST(Ref, InitialPayloadValue) {
|
||||||
RCTest* testDefaultConstructor = new RCTest();
|
RCTest* testDefaultConstructor = new RCTest();
|
||||||
|
@ -255,4 +309,105 @@ TEST(Ref, Detach) {
|
||||||
|
|
||||||
detached->Release();
|
detached->Release();
|
||||||
EXPECT_TRUE(deleted);
|
EXPECT_TRUE(deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test constructor passed a derived pointer
|
||||||
|
TEST(Ref, DerivedPointerConstructor) {
|
||||||
|
bool deleted = false;
|
||||||
|
{
|
||||||
|
Ref<RCTest> test(new RCTestDerived(&deleted));
|
||||||
|
test->Release();
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test copy constructor of derived class
|
||||||
|
TEST(Ref, DerivedCopyConstructor) {
|
||||||
|
bool deleted = false;
|
||||||
|
Ref<RCTestDerived> testDerived(new RCTestDerived(&deleted));
|
||||||
|
testDerived->Release();
|
||||||
|
|
||||||
|
{
|
||||||
|
Ref<RCTest> testBase(testDerived);
|
||||||
|
EXPECT_EQ(testBase->GetRefCountForTesting(), 2u);
|
||||||
|
EXPECT_EQ(testDerived->GetRefCountForTesting(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(testDerived->GetRefCountForTesting(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Ref constructed with nullptr
|
||||||
|
TEST(Ref, ConstructedWithNullptr) {
|
||||||
|
Ref<RCTest> test(nullptr);
|
||||||
|
EXPECT_EQ(test.Get(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Ref's copy assignment with derived class
|
||||||
|
TEST(Ref, CopyAssignmentDerived) {
|
||||||
|
bool deleted = false;
|
||||||
|
|
||||||
|
RCTestDerived* original = new RCTestDerived(&deleted);
|
||||||
|
Ref<RCTestDerived> source(original);
|
||||||
|
original->Release();
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
|
Ref<RCTest> destination;
|
||||||
|
destination = source;
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
|
EXPECT_EQ(source.Get(), original);
|
||||||
|
EXPECT_EQ(destination.Get(), original);
|
||||||
|
|
||||||
|
source = nullptr;
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
EXPECT_FALSE(deleted);
|
||||||
|
|
||||||
|
destination = nullptr;
|
||||||
|
EXPECT_TRUE(deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Ref's move constructor with derived class
|
||||||
|
TEST(Ref, MoveConstructorDerived) {
|
||||||
|
bool deleted = false;
|
||||||
|
RCTestDerived* original = new RCTestDerived(&deleted);
|
||||||
|
|
||||||
|
Ref<RCTestDerived> source(original);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
|
Ref<RCTest> destination(std::move(source));
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
|
original->Release();
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
|
EXPECT_EQ(source.Get(), nullptr);
|
||||||
|
EXPECT_EQ(destination.Get(), original);
|
||||||
|
EXPECT_FALSE(deleted);
|
||||||
|
|
||||||
|
destination = nullptr;
|
||||||
|
EXPECT_TRUE(deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Ref's move assignment with derived class
|
||||||
|
TEST(Ref, MoveAssignmentDerived) {
|
||||||
|
bool deleted = false;
|
||||||
|
RCTestDerived* original = new RCTestDerived(&deleted);
|
||||||
|
|
||||||
|
Ref<RCTestDerived> source(original);
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 2u);
|
||||||
|
|
||||||
|
original->Release();
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
|
Ref<RCTest> destination;
|
||||||
|
destination = std::move(source);
|
||||||
|
|
||||||
|
EXPECT_EQ(original->GetRefCountForTesting(), 1u);
|
||||||
|
|
||||||
|
EXPECT_EQ(source.Get(), nullptr);
|
||||||
|
EXPECT_EQ(destination.Get(), original);
|
||||||
|
EXPECT_FALSE(deleted);
|
||||||
|
|
||||||
|
destination = nullptr;
|
||||||
|
EXPECT_TRUE(deleted);
|
||||||
}
|
}
|
Loading…
Reference in New Issue