diff --git a/include/PrettyMemory.h b/include/PrettyMemory.h index d523b37..ddfe3b3 100644 --- a/include/PrettyMemory.h +++ b/include/PrettyMemory.h @@ -79,6 +79,7 @@ namespace prtm { if (other.m_pControlBlock) { + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; ++m_pControlBlock->ShadowCount; } @@ -94,6 +95,7 @@ namespace prtm { if (other.m_pControlBlock) { + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; ++m_pControlBlock->ShadowCount; } @@ -107,7 +109,9 @@ namespace prtm template, int> = 0> ShadowPtr(ShadowPtr&& other) noexcept { + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; + other.m_pTyped = nullptr; other.m_pControlBlock = nullptr; } @@ -121,6 +125,7 @@ namespace prtm if (this != &other) { Destroy(); + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; if (m_pControlBlock) { @@ -142,6 +147,7 @@ namespace prtm if (static_cast(this) != static_cast(&other)) { Destroy(); + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; if (m_pControlBlock) { @@ -161,7 +167,9 @@ namespace prtm if (this != &other) { Destroy(); + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; + other.m_pTyped = nullptr; other.m_pControlBlock = nullptr; } return *this; @@ -179,7 +187,9 @@ namespace prtm if (static_cast(this) != static_cast(&other)) { Destroy(); + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; + other.m_pTyped = nullptr; other.m_pControlBlock = nullptr; } return *this; @@ -192,10 +202,10 @@ namespace prtm std::size_t ShadowCount() const { return m_pControlBlock ? m_pControlBlock->ShadowCount : 0; } /** @brief Get the writable raw pointer, or nullptr if the object has expired. */ - Pointer Get() { return m_pControlBlock ? static_cast(m_pControlBlock->Data) : nullptr; } + Pointer Get() { return (m_pControlBlock && m_pControlBlock->Data) ? m_pTyped : nullptr; } /** @brief Get the const raw pointer, or nullptr if the object has expired. */ - ConstPointer Get() const { return m_pControlBlock ? static_cast(m_pControlBlock->Data) : nullptr; } + ConstPointer Get() const { return (m_pControlBlock && m_pControlBlock->Data) ? m_pTyped : nullptr; } /** @brief Get the writable data pointer. */ Pointer Data() { return Get(); } @@ -230,6 +240,7 @@ namespace prtm */ void Swap(ShadowPtr& other) noexcept { + std::swap(m_pTyped, other.m_pTyped); std::swap(m_pControlBlock, other.m_pControlBlock); } @@ -249,10 +260,12 @@ namespace prtm } m_pControlBlock = nullptr; } + m_pTyped = nullptr; } private: + Pointer m_pTyped{ nullptr }; detail::ControlBlock* m_pControlBlock{ nullptr }; }; @@ -316,28 +329,28 @@ namespace prtm template bool operator<(const ShadowPtr& lhs, std::nullptr_t) { - return lhs.Get() < nullptr; + return lhs.Get() < static_cast::ConstPointer>(nullptr); } /** @brief Compare a ShadowPtr with nullptr by raw pointer address. */ template bool operator<=(const ShadowPtr& lhs, std::nullptr_t) { - return lhs.Get() <= nullptr; + return lhs.Get() <= static_cast::ConstPointer>(nullptr); } /** @brief Compare a ShadowPtr with nullptr by raw pointer address. */ template bool operator>(const ShadowPtr& lhs, std::nullptr_t) { - return lhs.Get() > nullptr; + return lhs.Get() > static_cast::ConstPointer>(nullptr); } /** @brief Compare a ShadowPtr with nullptr by raw pointer address. */ template bool operator>=(const ShadowPtr& lhs, std::nullptr_t) { - return lhs.Get() >= nullptr; + return lhs.Get() >= static_cast::ConstPointer>(nullptr); } /** @brief Compare nullptr with a ShadowPtr. */ @@ -358,28 +371,28 @@ namespace prtm template bool operator<(std::nullptr_t, const ShadowPtr& rhs) { - return nullptr < rhs.Get(); + return static_cast::ConstPointer>(nullptr) < rhs.Get(); } /** @brief Compare nullptr with a ShadowPtr by raw pointer address. */ template bool operator<=(std::nullptr_t, const ShadowPtr& rhs) { - return nullptr <= rhs.Get(); + return static_cast::ConstPointer>(nullptr) <= rhs.Get(); } /** @brief Compare nullptr with a ShadowPtr by raw pointer address. */ template bool operator>(std::nullptr_t, const ShadowPtr& rhs) { - return nullptr > rhs.Get(); + return static_cast::ConstPointer>(nullptr) > rhs.Get(); } /** @brief Compare nullptr with a ShadowPtr by raw pointer address. */ template bool operator>=(std::nullptr_t, const ShadowPtr& rhs) { - return nullptr >= rhs.Get(); + return static_cast::ConstPointer>(nullptr) >= rhs.Get(); } /** @@ -401,6 +414,7 @@ namespace prtm } ShadowPtr shadow; + shadow.m_pTyped = static_cast(this); shadow.m_pControlBlock = m_pControlBlock; ++m_pControlBlock->ShadowCount; return shadow; @@ -416,6 +430,7 @@ namespace prtm } ShadowPtr shadow; + shadow.m_pTyped = const_cast(static_cast(this)); shadow.m_pControlBlock = m_pControlBlock; ++m_pControlBlock->ShadowCount; return shadow; @@ -467,7 +482,6 @@ namespace prtm using ConstPointer = const ValueType*; using Reference = ValueType&; using ConstReference = const ValueType&; - using DeleterType = DT; template friend class ShadowPtr; @@ -508,6 +522,7 @@ namespace prtm { if (pOther) { + m_pTyped = pOther; m_pControlBlock = new detail::ControlBlock; m_pControlBlock->Data = pOther; m_pControlBlock->Deleter = DT2{}; @@ -531,7 +546,9 @@ namespace prtm template, std::enable_if_t, int> = 0> OwnerPtr(OwnerPtr&& other) noexcept { + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; + other.m_pTyped = nullptr; other.m_pControlBlock = nullptr; } @@ -548,7 +565,9 @@ namespace prtm if (static_cast(this) != static_cast(&other)) { Destroy(); + m_pTyped = other.m_pTyped; m_pControlBlock = other.m_pControlBlock; + other.m_pTyped = nullptr; other.m_pControlBlock = nullptr; } return *this; @@ -561,10 +580,10 @@ namespace prtm std::size_t ShadowCount() const { return m_pControlBlock ? m_pControlBlock->ShadowCount : 0; } /** @brief Get the writable raw pointer. */ - Pointer Get() { return m_pControlBlock ? static_cast(m_pControlBlock->Data) : nullptr; } + Pointer Get() { return m_pTyped; } /** @brief Get the const raw pointer. */ - ConstPointer Get() const { return m_pControlBlock ? static_cast(m_pControlBlock->Data) : nullptr; } + ConstPointer Get() const { return m_pTyped; } /** @brief Get the writable data pointer. */ Pointer Data() { return Get(); } @@ -611,6 +630,7 @@ namespace prtm Destroy(); if (nullptr != pNew) { + m_pTyped = pNew; m_pControlBlock = new detail::ControlBlock; m_pControlBlock->Data = pNew; m_pControlBlock->Deleter = DT2{}; @@ -620,7 +640,7 @@ namespace prtm /** @brief Release ownership and return the raw pointer. */ [[nodiscard]] Pointer Release() { - Pointer pReleased = Get(); + Pointer pReleased = m_pTyped; if (m_pControlBlock) { m_pControlBlock->Data = nullptr; @@ -630,6 +650,7 @@ namespace prtm } m_pControlBlock = nullptr; } + m_pTyped = nullptr; return pReleased; } @@ -641,6 +662,7 @@ namespace prtm template void Swap(OwnerPtr& other) noexcept { + std::swap(m_pTyped, other.m_pTyped); std::swap(m_pControlBlock, other.m_pControlBlock); } @@ -656,7 +678,9 @@ namespace prtm OwnerPtr transferred; if (m_pControlBlock) { + transferred.m_pTyped = m_pTyped; transferred.m_pControlBlock = m_pControlBlock; + m_pTyped = nullptr; m_pControlBlock = nullptr; } return transferred; @@ -672,11 +696,12 @@ namespace prtm [[nodiscard]] OwnerPtr Cast() { OwnerPtr casted; - Pointer pCurrent = Get(); - typename OwnerPtr::Pointer pCasted = dynamic_cast::Pointer>(pCurrent); + typename OwnerPtr::Pointer pCasted = dynamic_cast::Pointer>(m_pTyped); if (pCasted) { + casted.m_pTyped = pCasted; casted.m_pControlBlock = m_pControlBlock; + m_pTyped = nullptr; m_pControlBlock = nullptr; } else @@ -697,6 +722,7 @@ namespace prtm ShadowPtr shadow; if (m_pControlBlock) { + shadow.m_pTyped = m_pTyped; shadow.m_pControlBlock = m_pControlBlock; ++m_pControlBlock->ShadowCount; } @@ -714,6 +740,7 @@ namespace prtm ShadowPtr shadow; if (m_pControlBlock) { + shadow.m_pTyped = m_pTyped; shadow.m_pControlBlock = m_pControlBlock; ++m_pControlBlock->ShadowCount; } @@ -737,10 +764,12 @@ namespace prtm } m_pControlBlock = nullptr; } + m_pTyped = nullptr; } private: + Pointer m_pTyped{ nullptr }; detail::ControlBlock* m_pControlBlock{ nullptr }; }; @@ -804,28 +833,28 @@ namespace prtm template> bool operator<(const OwnerPtr& lhs, std::nullptr_t) { - return lhs.Get() < nullptr; + return lhs.Get() < static_cast::ConstPointer>(nullptr); } /** @brief Compare an OwnerPtr with nullptr by raw pointer address. */ template> bool operator>(const OwnerPtr& lhs, std::nullptr_t) { - return lhs.Get() > nullptr; + return lhs.Get() > static_cast::ConstPointer>(nullptr); } /** @brief Compare an OwnerPtr with nullptr by raw pointer address. */ template> bool operator<=(const OwnerPtr& lhs, std::nullptr_t) { - return lhs.Get() <= nullptr; + return lhs.Get() <= static_cast::ConstPointer>(nullptr); } /** @brief Compare an OwnerPtr with nullptr by raw pointer address. */ template> bool operator>=(const OwnerPtr& lhs, std::nullptr_t) { - return lhs.Get() >= nullptr; + return lhs.Get() >= static_cast::ConstPointer>(nullptr); } /** @brief Compare nullptr with an OwnerPtr. */ @@ -846,28 +875,28 @@ namespace prtm template> bool operator<(std::nullptr_t, const OwnerPtr& rhs) { - return nullptr < rhs.Get(); + return static_cast::ConstPointer>(nullptr) < rhs.Get(); } /** @brief Compare nullptr with an OwnerPtr by raw pointer address. */ template> bool operator>(std::nullptr_t, const OwnerPtr& rhs) { - return nullptr > rhs.Get(); + return static_cast::ConstPointer>(nullptr) > rhs.Get(); } /** @brief Compare nullptr with an OwnerPtr by raw pointer address. */ template> bool operator<=(std::nullptr_t, const OwnerPtr& rhs) { - return nullptr <= rhs.Get(); + return static_cast::ConstPointer>(nullptr) <= rhs.Get(); } /** @brief Compare nullptr with an OwnerPtr by raw pointer address. */ template> bool operator>=(std::nullptr_t, const OwnerPtr& rhs) { - return nullptr >= rhs.Get(); + return static_cast::ConstPointer>(nullptr) >= rhs.Get(); } } diff --git a/test/OwnerPtr.Test.cpp b/test/OwnerPtr.Test.cpp index a3ad2d3..b95c275 100644 --- a/test/OwnerPtr.Test.cpp +++ b/test/OwnerPtr.Test.cpp @@ -682,6 +682,36 @@ DEFINE_TEST_BEGIN(OwnerPtrTest, Cast, ToRelatedType) } DEFINE_TEST_END +DEFINE_TEST_BEGIN(OwnerPtrTest, Cast, Offset) +{ + class BaseObject + { + public: + BaseObject(int value) : m_value(value) {} + ~BaseObject() = default; + int GetValue() const { return m_value; } + private: + int m_value{ 0 }; + }; + + class DerivedObject : public BaseObject, public TestableObject + { + public: + DerivedObject(int value) : BaseObject{ value } {} + }; + + OwnerPtr d = OwnerPtr::Create(42); + EXPECT_EQ(TestableObject::Balance, 1); + EXPECT_NE(d.Get(), nullptr); + + OwnerPtr b = d.Cast(); + EXPECT_EQ(TestableObject::Balance, 1); + EXPECT_EQ(d.Get(), nullptr); + EXPECT_NE(b.Get(), nullptr); + EXPECT_EQ(b->GetValue(), 42); +} +DEFINE_TEST_END + DEFINE_TEST_BEGIN(OwnerPtrTest, Cast, ToUnrelatedType) { class UnrelatedObject @@ -724,12 +754,13 @@ DEFINE_TEST_BEGIN(OwnerPtrTest, Comparison, NullptrOnRight) OwnerPtr emptyObj; EXPECT_EQ(TestableObject::Balance, 0); - EXPECT_EQ(emptyObj == nullptr, emptyObj.Get() == nullptr); - EXPECT_EQ(emptyObj != nullptr, emptyObj.Get() != nullptr); - EXPECT_EQ(emptyObj < nullptr, emptyObj.Get() < nullptr); - EXPECT_EQ(emptyObj > nullptr, emptyObj.Get() > nullptr); - EXPECT_EQ(emptyObj <= nullptr, emptyObj.Get() <= nullptr); - EXPECT_EQ(emptyObj >= nullptr, emptyObj.Get() >= nullptr); + TestableObject* const pNull = nullptr; + EXPECT_EQ(emptyObj == nullptr, emptyObj.Get() == pNull); + EXPECT_EQ(emptyObj != nullptr, emptyObj.Get() != pNull); + EXPECT_EQ(emptyObj < nullptr, emptyObj.Get() < pNull); + EXPECT_EQ(emptyObj > nullptr, emptyObj.Get() > pNull); + EXPECT_EQ(emptyObj <= nullptr, emptyObj.Get() <= pNull); + EXPECT_EQ(emptyObj >= nullptr, emptyObj.Get() >= pNull); } DEFINE_TEST_END @@ -738,11 +769,12 @@ DEFINE_TEST_BEGIN(OwnerPtrTest, Comparison, NullptrOnLeft) OwnerPtr emptyObj; EXPECT_EQ(TestableObject::Balance, 0); - EXPECT_EQ(nullptr == emptyObj, nullptr == emptyObj.Get()); - EXPECT_EQ(nullptr != emptyObj, nullptr != emptyObj.Get()); - EXPECT_EQ(nullptr < emptyObj, nullptr < emptyObj.Get()); - EXPECT_EQ(nullptr > emptyObj, nullptr > emptyObj.Get()); - EXPECT_EQ(nullptr <= emptyObj, nullptr <= emptyObj.Get()); - EXPECT_EQ(nullptr >= emptyObj, nullptr >= emptyObj.Get()); + TestableObject* const pNull = nullptr; + EXPECT_EQ(nullptr == emptyObj, pNull == emptyObj.Get()); + EXPECT_EQ(nullptr != emptyObj, pNull != emptyObj.Get()); + EXPECT_EQ(nullptr < emptyObj, pNull < emptyObj.Get()); + EXPECT_EQ(nullptr > emptyObj, pNull > emptyObj.Get()); + EXPECT_EQ(nullptr <= emptyObj, pNull <= emptyObj.Get()); + EXPECT_EQ(nullptr >= emptyObj, pNull >= emptyObj.Get()); } DEFINE_TEST_END diff --git a/test/ShadowPtr.Test.cpp b/test/ShadowPtr.Test.cpp index da24612..07f33d7 100644 --- a/test/ShadowPtr.Test.cpp +++ b/test/ShadowPtr.Test.cpp @@ -559,6 +559,33 @@ DEFINE_TEST_BEGIN(ShadowPtrTest, Shadow, FromDerivedOwner) } DEFINE_TEST_END +DEFINE_TEST_BEGIN(ShadowPtrTest, Shadow, Offset) +{ + class BaseObject + { + public: + BaseObject(int value) : m_value(value) {} + ~BaseObject() = default; + int GetValue() const { return m_value; } + private: + int m_value{ 0 }; + }; + + class DerivedObject : public BaseObject, public TestableObject + { + public: + DerivedObject(int value) : BaseObject{ value } {} + }; + + OwnerPtr owner = OwnerPtr::Create(42); + EXPECT_EQ(TestableObject::Balance, 1); + + ShadowPtr shadow = owner.Shadow(); + EXPECT_NE(shadow.Get(), nullptr); + EXPECT_EQ(shadow->GetValue(), 42); +} +DEFINE_TEST_END + // ShadowPtr comparison operators DEFINE_TEST_BEGIN(ShadowPtrTest, Comparison, ShadowPtr) @@ -583,12 +610,13 @@ DEFINE_TEST_BEGIN(ShadowPtrTest, Comparison, NullptrOnRight) ShadowPtr emptyObj; EXPECT_EQ(TestableObject::Balance, 0); - EXPECT_EQ(emptyObj == nullptr, emptyObj.Get() == nullptr); - EXPECT_EQ(emptyObj != nullptr, emptyObj.Get() != nullptr); - EXPECT_EQ(emptyObj < nullptr, emptyObj.Get() < nullptr); - EXPECT_EQ(emptyObj > nullptr, emptyObj.Get() > nullptr); - EXPECT_EQ(emptyObj <= nullptr, emptyObj.Get() <= nullptr); - EXPECT_EQ(emptyObj >= nullptr, emptyObj.Get() >= nullptr); + TestableObject* const pNull = nullptr; + EXPECT_EQ(emptyObj == nullptr, emptyObj.Get() == pNull); + EXPECT_EQ(emptyObj != nullptr, emptyObj.Get() != pNull); + EXPECT_EQ(emptyObj < nullptr, emptyObj.Get() < pNull); + EXPECT_EQ(emptyObj > nullptr, emptyObj.Get() > pNull); + EXPECT_EQ(emptyObj <= nullptr, emptyObj.Get() <= pNull); + EXPECT_EQ(emptyObj >= nullptr, emptyObj.Get() >= pNull); } DEFINE_TEST_END @@ -597,12 +625,13 @@ DEFINE_TEST_BEGIN(ShadowPtrTest, Comparison, NullptrOnLeft) ShadowPtr emptyObj; EXPECT_EQ(TestableObject::Balance, 0); - EXPECT_EQ(nullptr == emptyObj, nullptr == emptyObj.Get()); - EXPECT_EQ(nullptr != emptyObj, nullptr != emptyObj.Get()); - EXPECT_EQ(nullptr < emptyObj, nullptr < emptyObj.Get()); - EXPECT_EQ(nullptr > emptyObj, nullptr > emptyObj.Get()); - EXPECT_EQ(nullptr <= emptyObj, nullptr <= emptyObj.Get()); - EXPECT_EQ(nullptr >= emptyObj, nullptr >= emptyObj.Get()); + TestableObject* const pNull = nullptr; + EXPECT_EQ(nullptr == emptyObj, pNull == emptyObj.Get()); + EXPECT_EQ(nullptr != emptyObj, pNull != emptyObj.Get()); + EXPECT_EQ(nullptr < emptyObj, pNull < emptyObj.Get()); + EXPECT_EQ(nullptr > emptyObj, pNull > emptyObj.Get()); + EXPECT_EQ(nullptr <= emptyObj, pNull <= emptyObj.Get()); + EXPECT_EQ(nullptr >= emptyObj, pNull >= emptyObj.Get()); } DEFINE_TEST_END