From 481c45794f9c385b53fc39e44740e392de2eeb76 Mon Sep 17 00:00:00 2001 From: kunitoki Date: Sun, 1 Dec 2024 09:47:47 +0100 Subject: [PATCH] Allow using const reference counted object ptrs (#28) --- .../memory/juce_ReferenceCountedObject.h | 18 +- .../juce_core/juce_ReferenceCountedObject.cpp | 226 ++++++++++++++++++ 2 files changed, 236 insertions(+), 8 deletions(-) create mode 100644 tests/juce_core/juce_ReferenceCountedObject.cpp diff --git a/modules/juce_core/memory/juce_ReferenceCountedObject.h b/modules/juce_core/memory/juce_ReferenceCountedObject.h index 27cbd441..cf9b8ae5 100644 --- a/modules/juce_core/memory/juce_ReferenceCountedObject.h +++ b/modules/juce_core/memory/juce_ReferenceCountedObject.h @@ -88,7 +88,7 @@ class JUCE_API ReferenceCountedObject This is done automatically by the smart pointer, but is public just in case it's needed for nefarious purposes. */ - void incReferenceCount() noexcept + void incReferenceCount() const noexcept { ++refCount; } @@ -96,7 +96,7 @@ class JUCE_API ReferenceCountedObject /** Decreases the object's reference count. If the count gets to zero, the object will be deleted. */ - void decReferenceCount() noexcept + void decReferenceCount() const noexcept { jassert (getReferenceCount() > 0); @@ -108,7 +108,7 @@ class JUCE_API ReferenceCountedObject If the count gets to zero, the object will not be deleted, but this method will return true, allowing the caller to take care of deletion. */ - bool decReferenceCountWithoutDeleting() noexcept + bool decReferenceCountWithoutDeleting() const noexcept { jassert (getReferenceCount() > 0); return --refCount == 0; @@ -151,7 +151,8 @@ class JUCE_API ReferenceCountedObject private: //============================================================================== - Atomic refCount { 0 }; + mutable Atomic refCount { 0 }; + friend struct ContainerDeletePolicy; }; @@ -177,7 +178,7 @@ class JUCE_API SingleThreadedReferenceCountedObject This is done automatically by the smart pointer, but is public just in case it's needed for nefarious purposes. */ - void incReferenceCount() noexcept + void incReferenceCount() const noexcept { ++refCount; } @@ -185,7 +186,7 @@ class JUCE_API SingleThreadedReferenceCountedObject /** Decreases the object's reference count. If the count gets to zero, the object will be deleted. */ - void decReferenceCount() noexcept + void decReferenceCount() const noexcept { jassert (getReferenceCount() > 0); @@ -197,7 +198,7 @@ class JUCE_API SingleThreadedReferenceCountedObject If the count gets to zero, the object will not be deleted, but this method will return true, allowing the caller to take care of deletion. */ - bool decReferenceCountWithoutDeleting() noexcept + bool decReferenceCountWithoutDeleting() const noexcept { jassert (getReferenceCount() > 0); return --refCount == 0; @@ -232,7 +233,8 @@ class JUCE_API SingleThreadedReferenceCountedObject private: //============================================================================== - int refCount = 0; + mutable int refCount = 0; + friend struct ContainerDeletePolicy; }; diff --git a/tests/juce_core/juce_ReferenceCountedObject.cpp b/tests/juce_core/juce_ReferenceCountedObject.cpp new file mode 100644 index 00000000..e8e1d2a3 --- /dev/null +++ b/tests/juce_core/juce_ReferenceCountedObject.cpp @@ -0,0 +1,226 @@ +/* + ============================================================================== + This file is part of the YUP library. + Copyright (c) 2024 - kunitoki@gmail.com + YUP is an open source library subject to open-source licensing. + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + ============================================================================== +*/ + +#include + +#include + +using namespace juce; + +class TestClass : public ReferenceCountedObject +{ +public: + using Ptr = ReferenceCountedObjectPtr; + using ConstPtr = ReferenceCountedObjectPtr; + + void doSomething() const {} +}; + +class SingleThreadedTestClass : public SingleThreadedReferenceCountedObject +{ +public: + using Ptr = ReferenceCountedObjectPtr; + using ConstPtr = ReferenceCountedObjectPtr; + + void doSomething() const {} +}; + +TEST (ReferenceCountedObjectTests, IncDecReferenceCount) +{ + TestClass::Ptr obj = new TestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj->incReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 2); + + obj->decReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj = nullptr; // this should decrement the count and delete the object +} + +TEST (ReferenceCountedObjectTests, IncDecConstReferenceCount) +{ + TestClass::ConstPtr obj = new TestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj->incReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 2); + + obj->decReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj = nullptr; // this should decrement the count and delete the object +} + +TEST (ReferenceCountedObjectTests, DecReferenceCountWithoutDeleting) +{ + TestClass::Ptr obj = new TestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + EXPECT_TRUE (obj->decReferenceCountWithoutDeleting()); + EXPECT_EQ (obj->getReferenceCount(), 0); + + delete obj; +} + +TEST (ReferenceCountedObjectTests, DecConstReferenceCountWithoutDeleting) +{ + TestClass::ConstPtr obj = new TestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + EXPECT_TRUE (obj->decReferenceCountWithoutDeleting()); + EXPECT_EQ (obj->getReferenceCount(), 0); + + delete obj; +} + +// SingleThreadedReferenceCountedObject tests +TEST (SingleThreadedReferenceCountedObjectTests, IncDecReferenceCount) +{ + SingleThreadedTestClass::Ptr obj = new SingleThreadedTestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj->incReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 2); + + obj->decReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj = nullptr; // this should decrement the count and delete the object +} + +TEST (SingleThreadedReferenceCountedObjectTests, IncDecConstReferenceCount) +{ + SingleThreadedTestClass::ConstPtr obj = new SingleThreadedTestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj->incReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 2); + + obj->decReferenceCount(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj = nullptr; // this should decrement the count and delete the object +} + +TEST (SingleThreadedReferenceCountedObjectTests, DecReferenceCountWithoutDeleting) +{ + SingleThreadedTestClass::Ptr obj = new SingleThreadedTestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + EXPECT_TRUE (obj->decReferenceCountWithoutDeleting()); + EXPECT_EQ (obj->getReferenceCount(), 0); + + delete obj; +} + +TEST (SingleThreadedReferenceCountedObjectTests, DecConstReferenceCountWithoutDeleting) +{ + SingleThreadedTestClass::ConstPtr obj = new SingleThreadedTestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + EXPECT_TRUE (obj->decReferenceCountWithoutDeleting()); + EXPECT_EQ (obj->getReferenceCount(), 0); + + delete obj; +} + +// ReferenceCountedObjectPtr tests +TEST (ReferenceCountedObjectPtrTests, PointerAssignment) +{ + TestClass::Ptr obj1 = new TestClass(); + TestClass::Ptr obj2 = obj1; + + EXPECT_EQ (obj1->getReferenceCount(), 2); + EXPECT_EQ (obj2->getReferenceCount(), 2); + + obj1 = nullptr; + EXPECT_EQ (obj2->getReferenceCount(), 1); + obj2 = nullptr; // this should delete the object +} + +TEST (ReferenceCountedObjectPtrTests, PointerComparison) +{ + TestClass::Ptr obj1 = new TestClass(); + TestClass::Ptr obj2 = obj1; + + EXPECT_EQ (obj1, obj2); + EXPECT_NE (obj1, nullptr); + + obj1 = nullptr; + EXPECT_EQ (obj1, nullptr); + EXPECT_NE (obj2, nullptr); +} + +TEST (ReferenceCountedObjectPtrTests, PointerDereference) +{ + TestClass::Ptr obj = new TestClass(); + EXPECT_NO_THROW (obj->doSomething()); + EXPECT_NO_THROW (*obj); +} + +TEST (ReferenceCountedObjectPtrTests, Reset) +{ + TestClass::Ptr obj = new TestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj.reset(); + EXPECT_EQ (obj, nullptr); +} + +// SingleThreadedReferenceCountedObjectPtr tests +TEST (SingleThreadedReferenceCountedObjectPtrTests, PointerAssignment) +{ + SingleThreadedTestClass::Ptr obj1 = new SingleThreadedTestClass(); + SingleThreadedTestClass::Ptr obj2 = obj1; + + EXPECT_EQ (obj1->getReferenceCount(), 2); + EXPECT_EQ (obj2->getReferenceCount(), 2); + + obj1 = nullptr; + EXPECT_EQ (obj2->getReferenceCount(), 1); + obj2 = nullptr; // this should delete the object +} + +TEST (SingleThreadedReferenceCountedObjectPtrTests, PointerComparison) +{ + SingleThreadedTestClass::Ptr obj1 = new SingleThreadedTestClass(); + SingleThreadedTestClass::Ptr obj2 = obj1; + + EXPECT_EQ (obj1, obj2); + EXPECT_NE (obj1, nullptr); + + obj1 = nullptr; + EXPECT_EQ (obj1, nullptr); + EXPECT_NE (obj2, nullptr); +} + +TEST (SingleThreadedReferenceCountedObjectPtrTests, PointerDereference) +{ + SingleThreadedTestClass::Ptr obj = new SingleThreadedTestClass(); + EXPECT_NO_THROW (obj->doSomething()); + EXPECT_NO_THROW (*obj); +} + +TEST (SingleThreadedReferenceCountedObjectPtrTests, Reset) +{ + SingleThreadedTestClass::Ptr obj = new SingleThreadedTestClass(); + EXPECT_EQ (obj->getReferenceCount(), 1); + + obj.reset(); + EXPECT_EQ (obj, nullptr); +}