[common] adding OwnedPtr and RetainPtr classes (#7171)

This commit adds two smart pointer types `OwnedPtr` and `RetainPtr`.

`OwnedPtr` acts as the sole owner of the object it manages. It is
non-copyable but the ownership can be transferred from one `OwnedPtr`
to another using move semantics.

`RetainPtr` is an intrusive reference counted smart pointer allowing
multiple pointers to share management of the same object. It requires
the underlying `Type` object to provide mechanism to track the
current retain count. This may be realized by the `Type` itself
providing this or by having it be a sub-class of the newly added
`RetainCountable` class.

This commit also add a unit test `test_smart_ptr` validating the
behavior of newly added classes.
This commit is contained in:
Abtin Keshavarzian
2021-11-19 10:33:19 -08:00
committed by GitHub
parent ccceb5f054
commit c127d99668
7 changed files with 1031 additions and 0 deletions
+2
View File
@@ -409,10 +409,12 @@ openthread_core_files = [
"common/notifier.cpp",
"common/notifier.hpp",
"common/numeric_limits.hpp",
"common/owned_ptr.hpp",
"common/pool.hpp",
"common/random.hpp",
"common/random_manager.cpp",
"common/random_manager.hpp",
"common/retain_ptr.hpp",
"common/settings.cpp",
"common/settings.hpp",
"common/settings_driver.hpp",
+2
View File
@@ -443,9 +443,11 @@ HEADERS_COMMON = \
common/non_copyable.hpp \
common/notifier.hpp \
common/numeric_limits.hpp \
common/owned_ptr.hpp \
common/pool.hpp \
common/random.hpp \
common/random_manager.hpp \
common/retain_ptr.hpp \
common/settings.hpp \
common/settings_driver.hpp \
common/string.hpp \
+247
View File
@@ -0,0 +1,247 @@
/*
* Copyright (c) 2021, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for an owned smart pointer.
*/
#ifndef OWNED_PTR_HPP_
#define OWNED_PTR_HPP_
#include "openthread-core-config.h"
#include <stdbool.h>
#include <stdint.h>
namespace ot {
/**
* This template class represents an owned smart pointer.
*
* `OwnedPtr` acts as sole owner of the object it manages. An `OwnedPtr` is non-copyable (copy constructor is deleted)
* but the ownership can be transferred from one `OwnedPtr` to another using move semantics.
*
* The `Type` class MUST provide `Free()` method which frees the instance.
*
* @tparam Type The pointer type.
*
*/
template <class Type> class OwnedPtr
{
public:
/**
* This is the default constructor for `OwnedPtr` initializing it as null.
*
*/
OwnedPtr(void)
: mPointer(nullptr)
{
}
/**
* This constructor initializes the `OwnedPtr` with a given pointer.
*
* The `OwnedPtr` takes the ownership of the object at @p aPointer.
*
* @param[in] aPointer A pointer to object to initialize with.
*
*/
explicit OwnedPtr(Type *aPointer)
: mPointer(aPointer)
{
}
/**
* This constructor initializes the `OwnedPtr` from another `OwnedPtr` using move semantics.
*
* The `OwnedPtr` takes over the ownership of the object from @p aOther. After this call, @p aOther will be null.
*
* @param[in] aOther An rvalue reference to another `OwnedPtr`.
*
*/
OwnedPtr(OwnedPtr &&aOther)
{
mPointer = aOther.mPointer;
aOther.mPointer = nullptr;
}
/**
* This is the destructor for `OwnedPtr`.
*
* Upon destruction, the `OwnedPtr` invokes `Free()` method on its managed object (if any).
*
*/
~OwnedPtr(void) { Delete(); }
/**
* This method indicates whether the `OwnedPtr` is null or not.
*
* @retval TRUE The `OwnedPtr` is null.
* @retval FALSE The `OwnedPtr` is not null.
*
*/
bool IsNull(void) const { return (mPointer == nullptr); }
/**
* This method gets the raw pointer to the object owned by `OwnedPtr`.
*
* @returns The raw pointer to the object owned by `OwnedPtr` or `nullptr` if none.
*
*/
Type *Get(void) { return mPointer; }
/**
* This method gets the raw pointer to the object owned by `OwnedPtr`.
*
* @returns The raw pointer to the object owned by `OwnedPtr` or `nullptr` if none.
*
*/
const Type *Get(void) const { return mPointer; }
/**
* This method frees the owned object (if any).
*
* This method invokes `Free()` method on the `Type` object owned by `OwnedPtr` (if any). It will also set the
* `OwnedPtr` to null.
*
*/
void Free(void)
{
Delete();
mPointer = nullptr;
}
/**
* This method frees the current object owned by `OwnedPtr` (if any) and replaces it with a new one.
*
* The method will `Free()` the current object managed by `OwnedPtr` (if different from @p aPointer) before taking
* the ownership of the object at @p aPointer. The method correctly handles a self `Reset()` (i.e., @p aPointer
* being the same pointer as the one currently managed by `OwnedPtr`).
*
* @param[in] aPointer A pointer to the new object to replace with.
*
*/
void Reset(Type *aPointer = nullptr)
{
if (mPointer != aPointer)
{
Delete();
mPointer = aPointer;
}
}
/**
* This method releases the ownership of the current object in `OwnedPtr` (if any).
*
* After this call, the `OwnedPtr` will be null.
*
* @returns The pointer to the object owned by `OwnedPtr` or `nullptr` if `OwnedPtr` was null.
*
*/
Type *Release(void)
{
Type *pointer = mPointer;
mPointer = nullptr;
return pointer;
}
/**
* This method overloads the `->` dereference operator and returns a pointer to the object owned by `OwnedPtr`.
*
* @returns A pointer to owned object or `nullptr` if none.
*
*/
Type *operator->(void) { return mPointer; }
/**
* This method overloads the `->` dereference operator and returns a pointer to the object owned by `OwnedPtr`.
*
* @returns A pointer to owned object or `nullptr` if none.
*
*/
const Type *operator->(void)const { return mPointer; }
/**
* This method overloads the `*` dereference operator and returns a reference to the object owned by `OwnedPtr`.
*
* The behavior is undefined if `IsNull() == true`.
*
* @returns A reference to the object owned by `OwnedPtr`.
*
*/
Type &operator*(void) { return *mPointer; }
/**
* This method overloads the `*` dereference operator and returns a reference to the object owned by `OwnedPtr`.
*
* The behavior is undefined if `IsNull() == true`.
*
* @returns A reference to the object owned by `OwnedPtr`.
*
*/
const Type &operator*(void)const { return *mPointer; }
/**
* This method overload the assignment operator `=` to replace the object owned by the `OwnedPtr` with another one
* using move semantics.
*
* The `OwnedPtr` first frees its current owned object (if there is any and it is different from @p aOther) before
* taking over the ownership of the object from @p aOther. This method correctly handles a self assignment (i.e.,
* assigning the pointer to itself).
*
* @param[in] aOther An rvalue reference to an `OwnedPtr` to move from.
*
* @returns A reference to this `OwnedPtr`.
*
*/
OwnedPtr &operator=(OwnedPtr &&aOther)
{
Reset(aOther.Release());
return *this;
}
OwnedPtr(const OwnedPtr &) = delete;
OwnedPtr(OwnedPtr &) = delete;
OwnedPtr &operator=(const OwnedPtr &) = delete;
private:
void Delete(void)
{
if (mPointer != nullptr)
{
mPointer->Free();
}
}
Type *mPointer;
};
} // namespace ot
#endif // OWNED_PTR_HPP_
+286
View File
@@ -0,0 +1,286 @@
/*
* Copyright (c) 2021, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for a retain (reference counted) smart pointer.
*/
#ifndef RETAIN_PTR_HPP_
#define RETAIN_PTR_HPP_
#include "openthread-core-config.h"
#include <stdbool.h>
#include <stdint.h>
namespace ot {
/**
* This template class represents a retain (reference counted) smart pointer.
*
* The `Type` class MUST provide mechanism to track its current retain count. It MUST provide the following three
* methods:
*
* - void IncrementRetainCount(void); (Increment the retain count).
* - uint16_t DecrementRetainCount(void); (Decrement the retain count and return the value after decrement).
* - void Free(void); (Free the `Type` instance).
*
* The `Type` can inherit from `RetainCountable` which provides the retain counting methods.
*
* @tparam Type The pointer type.
*
*/
template <class Type> class RetainPtr
{
public:
/**
* This is the default constructor for `RetainPtr` initializing it as null.
*
*/
RetainPtr(void)
: mPointer(nullptr)
{
}
/**
* This constructor initializes the `RetainPtr` with a given pointer.
*
* Upon construction the `RetainPtr` will increment the retain count on @p aPointer (if not null).
*
* @param[in] aPointer A pointer to object to initialize with.
*
*/
explicit RetainPtr(Type *aPointer)
: mPointer(aPointer)
{
IncrementRetainCount();
}
/**
* This constructor initializes the `RetainPtr` from another `RetainPtr`.
*
* @param[in] aOther Another `RetainPtr`.
*
*/
RetainPtr(const RetainPtr &aOther)
: mPointer(aOther.mPointer)
{
IncrementRetainCount();
}
/**
* This is the destructor for `RetainPtr`.
*
* Upon destruction, the `RetainPtr` will decrement the retain count on the managed object (if not null) and
* free the object if its retain count reaches zero.
*
*/
~RetainPtr(void) { DecrementRetainCount(); }
/**
* This method indicates whether the `RetainPtr` is null or not.
*
* @retval TRUE The `RetainPtr` is null.
* @retval FALSE The `RetainPtr` is not null.
*
*/
bool IsNull(void) const { return (mPointer == nullptr); }
/**
* This method gets the raw pointer to the object managed by `RetainPtr`.
*
* @returns The raw pointer to the object managed by `RetainPtr` or `nullptr` if none.
*
*/
Type *Get(void) { return mPointer; }
/**
* This method gets the raw pointer to the object managed by `RetainPtr`.
*
* @returns The raw pointer to the object managed by `RetainPtr` or `nullptr` if none.
*
*/
const Type *Get(void) const { return mPointer; }
/**
* This method replaces the managed object by `RetainPtr` with a new one.
*
* The method correctly handles a self `Reset()` (i.e., @p aPointer being the same pointer as the one currently
* managed by `RetainPtr`).
*
* @param[in] aPointer A pointer to a new object to replace with.
*
*/
void Reset(Type *aPointer = nullptr)
{
if (aPointer != mPointer)
{
DecrementRetainCount();
mPointer = aPointer;
IncrementRetainCount();
}
}
/**
* This method releases the ownership of the current pointer in `RetainPtr` (if any) without changing its retain
* count.
*
* After this call, the `RetainPtr` will be null.
*
* @returns The pointer to the object managed by `RetainPtr` or `nullptr` if `RetainPtr` was null.
*
*/
Type *Release(void)
{
Type *pointer = mPointer;
mPointer = nullptr;
return pointer;
}
/**
* This method overloads the `->` dereference operator and returns a pointer to the object managed by `RetainPtr`.
*
* @returns A pointer to managed object or `nullptr` if none.
*
*/
Type *operator->(void) { return mPointer; }
/**
* This method overloads the `->` dereference operator and returns a pointer to the object managed by `RetainPtr`.
*
* @returns A pointer to managed object or `nullptr` if none.
*
*/
const Type *operator->(void)const { return mPointer; }
/**
* This method overloads the `*` dereference operator and returns a reference to the object managed by `RetainPtr`.
*
* The behavior is undefined if `IsNull() == true`.
*
* @returns A reference to the object managed by `RetainPtr`.
*
*/
Type &operator*(void) { return *mPointer; }
/**
* This method overloads the `*` dereference operator and returns a reference to the object managed by `RetainPtr`.
*
* The behavior is undefined if `IsNull() == true`.
*
* @returns A reference to the object managed by `RetainPtr`.
*
*/
const Type &operator*(void)const { return *mPointer; }
/**
* This method overload assignment operator `=` .
*
* The `RetainPtr` first frees its current managed object (if there is any and it is different from @p aOther)
* before taking over the ownership of the object from @p aOther. This method correctly handles a self assignment
* (i.e., assigning the pointer to itself).
*
* @param[in] aOther A reference to an `OwendPtr`.
*
* @returns A reference to this `RetainPtr`.
*
*/
RetainPtr &operator=(RetainPtr &aOther)
{
Reset(aOther.Get());
return *this;
}
private:
void IncrementRetainCount(void)
{
if (mPointer != nullptr)
{
mPointer->IncrementRetainCount();
}
}
void DecrementRetainCount(void)
{
if ((mPointer != nullptr) && (mPointer->DecrementRetainCount() == 0))
{
mPointer->Free();
}
}
Type *mPointer;
};
/**
* This class provides mechanism to track retain count.
*
*/
class RetainCountable
{
template <class Type> friend class RetainPtr;
protected:
/**
* This constrictor initializes the object starting with retain count of zero.
*
*/
RetainCountable(void)
: mRetainCount(0)
{
}
/**
* This method returns the current retain count.
*
* @returns The current retain count.
*
*/
uint16_t GetRetainCount(void) const { return mRetainCount; }
/**
* This method increments the retain count.
*
*/
void IncrementRetainCount(void) { ++mRetainCount; }
/**
* This method decrements the retain count.
*
* @returns The retain count value after decrementing it.
*
*/
uint16_t DecrementRetainCount(void) { return --mRetainCount; }
private:
uint16_t mRetainCount;
};
} // namespace ot
#endif // RETAIN_PTR_HPP_
+21
View File
@@ -705,6 +705,27 @@ target_link_libraries(ot-test-pskc
add_test(NAME ot-test-pskc COMMAND ot-test-pskc)
add_executable(ot-test-smart-ptrs
test_smart_ptrs.cpp
)
target_include_directories(ot-test-smart-ptrs
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-smart-ptrs
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-smart-ptrs
PRIVATE
${COMMON_LIBS}
)
add_test(NAME ot-test-smart-ptrs COMMAND ot-test-smart-ptrs)
add_executable(ot-test-meshcop
test_meshcop.cpp
)
+4
View File
@@ -139,6 +139,7 @@ check_PROGRAMS += \
ot-test-pool \
ot-test-priority-queue \
ot-test-pskc \
ot-test-smart-ptrs \
ot-test-string \
ot-test-timer \
$(NULL)
@@ -277,6 +278,9 @@ ot_test_priority_queue_SOURCES = $(COMMON_SOURCES) test_priority_queue.cpp
ot_test_pskc_LDADD = $(COMMON_LDADD)
ot_test_pskc_SOURCES = $(COMMON_SOURCES) test_pskc.cpp
ot_test_smart_ptrs_LDADD = $(COMMON_LDADD)
ot_test_smart_ptrs_SOURCES = $(COMMON_SOURCES) test_smart_ptrs.cpp
ot_test_meshcop_LDADD = $(COMMON_LDADD)
ot_test_meshcop_SOURCES = $(COMMON_SOURCES) test_meshcop.cpp
+469
View File
@@ -0,0 +1,469 @@
/*
* Copyright (c) 2021, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_platform.h"
#include <string.h>
#include <openthread/config.h>
#include "test_util.hpp"
#include "common/code_utils.hpp"
#include "common/owned_ptr.hpp"
#include "common/retain_ptr.hpp"
namespace ot {
class TestObject : public RetainCountable
{
public:
TestObject(void)
: mWasFreed(false)
{
}
void Free(void) { mWasFreed = true; }
void ResetTestFlags(void) { mWasFreed = false; }
uint16_t GetRetainCount(void) const { return RetainCountable::GetRetainCount(); }
bool WasFreed(void) const { return mWasFreed && (GetRetainCount() == 0); }
private:
bool mWasFreed;
};
static constexpr uint16_t kSkipRetainCountCheck = 0xffff;
template <typename PointerType>
void VerifyPointer(const PointerType &aPointer,
const TestObject * aObject,
uint16_t aRetainCount = kSkipRetainCountCheck)
{
if (aObject == nullptr)
{
VerifyOrQuit(aPointer.IsNull());
VerifyOrQuit(aPointer.Get() == nullptr);
}
else
{
VerifyOrQuit(!aPointer.IsNull());
VerifyOrQuit(aPointer.Get() == aObject);
VerifyOrQuit(!aPointer->WasFreed());
VerifyOrQuit(!(*aPointer).WasFreed());
if (aRetainCount != kSkipRetainCountCheck)
{
VerifyOrQuit(aObject->GetRetainCount() == aRetainCount);
}
}
}
void TestOwnedPtr(void)
{
TestObject obj1;
TestObject obj2;
TestObject obj3;
printf("\n====================================================================================\n");
printf("Testing `OwnedPtr`\n");
printf("\n - Default constructor (null pointer)");
{
OwnedPtr<TestObject> ptr;
VerifyPointer(ptr, nullptr);
}
printf("\n - Constructor taking ownership of an object");
obj1.ResetTestFlags();
{
OwnedPtr<TestObject> ptr(&obj1);
VerifyPointer(ptr, &obj1);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - Move constructor taking over from another");
obj1.ResetTestFlags();
{
OwnedPtr<TestObject> ptr1(&obj1);
OwnedPtr<TestObject> ptr2(static_cast<OwnedPtr<TestObject> &&>(ptr1));
VerifyPointer(ptr1, nullptr);
VerifyPointer(ptr2, &obj1);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - `Free()` method");
obj1.ResetTestFlags();
{
OwnedPtr<TestObject> ptr(&obj1);
VerifyPointer(ptr, &obj1);
ptr.Free();
VerifyOrQuit(obj1.WasFreed());
VerifyPointer(ptr, nullptr);
ptr.Free();
VerifyOrQuit(obj1.WasFreed());
VerifyPointer(ptr, nullptr);
}
printf("\n - `Reset()` method");
obj1.ResetTestFlags();
obj2.ResetTestFlags();
obj3.ResetTestFlags();
{
OwnedPtr<TestObject> ptr(&obj1);
VerifyPointer(ptr, &obj1);
ptr.Reset(&obj2);
VerifyOrQuit(obj1.WasFreed());
VerifyOrQuit(!obj2.WasFreed());
VerifyPointer(ptr, &obj2);
ptr.Reset();
VerifyOrQuit(obj2.WasFreed());
VerifyPointer(ptr, nullptr);
ptr.Reset(&obj3);
VerifyPointer(ptr, &obj3);
}
VerifyOrQuit(obj1.WasFreed());
VerifyOrQuit(obj2.WasFreed());
VerifyOrQuit(obj3.WasFreed());
printf("\n - Self `Reset()`");
obj1.ResetTestFlags();
{
OwnedPtr<TestObject> ptr1(&obj1);
OwnedPtr<TestObject> ptr2;
VerifyPointer(ptr1, &obj1);
ptr1.Reset(&obj1);
VerifyPointer(ptr1, &obj1);
ptr2.Reset(nullptr);
VerifyPointer(ptr2, nullptr);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - Move assignment (operator `=`)");
obj1.ResetTestFlags();
obj2.ResetTestFlags();
obj3.ResetTestFlags();
{
OwnedPtr<TestObject> ptr1(&obj1);
OwnedPtr<TestObject> ptr2(&obj2);
OwnedPtr<TestObject> ptr3(&obj3);
VerifyPointer(ptr1, &obj1);
VerifyPointer(ptr2, &obj2);
VerifyPointer(ptr3, &obj3);
// Move from non-null (ptr1) to non-null (ptr2)
ptr2 = static_cast<OwnedPtr<TestObject> &&>(ptr1);
VerifyPointer(ptr1, nullptr);
VerifyPointer(ptr2, &obj1);
VerifyOrQuit(!obj1.WasFreed());
VerifyOrQuit(obj2.WasFreed());
// Move from null (ptr1) to non-null (ptr3)
ptr3 = static_cast<OwnedPtr<TestObject> &&>(ptr1);
VerifyPointer(ptr1, nullptr);
VerifyPointer(ptr3, nullptr);
VerifyOrQuit(obj3.WasFreed());
// Move from non-null (ptr2) to null (ptr1)
ptr1 = static_cast<OwnedPtr<TestObject> &&>(ptr2);
VerifyPointer(ptr1, &obj1);
VerifyPointer(ptr2, nullptr);
VerifyOrQuit(!obj1.WasFreed());
// Move from null (ptr2) to null (ptr3)
ptr3 = static_cast<OwnedPtr<TestObject> &&>(ptr2);
VerifyPointer(ptr2, nullptr);
VerifyPointer(ptr3, nullptr);
VerifyOrQuit(!obj1.WasFreed());
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - Self move assignment (operator `=`)");
obj1.ResetTestFlags();
{
OwnedPtr<TestObject> ptr1(&obj1);
OwnedPtr<TestObject> ptr2;
VerifyPointer(ptr1, &obj1);
VerifyPointer(ptr2, nullptr);
// Move from non-null (ptr1) to itself
ptr1 = static_cast<OwnedPtr<TestObject> &&>(ptr1);
VerifyPointer(ptr1, &obj1);
// Move from null (ptr2) to itself
ptr2 = static_cast<OwnedPtr<TestObject> &&>(ptr2);
VerifyPointer(ptr2, nullptr);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - `Release()` method");
obj1.ResetTestFlags();
{
OwnedPtr<TestObject> ptr(&obj1);
VerifyPointer(ptr, &obj1);
VerifyOrQuit(ptr.Release() == &obj1);
VerifyOrQuit(!obj1.WasFreed());
VerifyPointer(ptr, nullptr);
VerifyOrQuit(ptr.Release() == nullptr);
VerifyOrQuit(!obj1.WasFreed());
VerifyPointer(ptr, nullptr);
}
printf("\n\n-- PASS\n");
}
void TestRetainPtr(void)
{
TestObject obj1;
TestObject obj2;
TestObject obj3;
printf("\n====================================================================================\n");
printf("Testing `RetainPtr`\n");
VerifyOrQuit(obj1.GetRetainCount() == 0);
VerifyOrQuit(obj2.GetRetainCount() == 0);
VerifyOrQuit(obj3.GetRetainCount() == 0);
printf("\n - Default constructor (null pointer)");
{
RetainPtr<TestObject> ptr;
VerifyPointer(ptr, nullptr);
}
printf("\n - Constructor taking over management of an object");
obj1.ResetTestFlags();
{
RetainPtr<TestObject> ptr(&obj1);
VerifyPointer(ptr, &obj1, 1);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - Two constructed `RetainPtr`s of the same object");
obj1.ResetTestFlags();
{
RetainPtr<TestObject> ptr1(&obj1);
RetainPtr<TestObject> ptr2(&obj1);
VerifyPointer(ptr1, &obj1, 2);
VerifyPointer(ptr2, &obj1, 2);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - Copy constructor");
obj1.ResetTestFlags();
{
RetainPtr<TestObject> ptr1(&obj1);
RetainPtr<TestObject> ptr2(ptr1);
VerifyPointer(ptr1, &obj1, 2);
VerifyPointer(ptr2, &obj1, 2);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - `Reset()` method");
obj1.ResetTestFlags();
obj2.ResetTestFlags();
obj3.ResetTestFlags();
{
RetainPtr<TestObject> ptr(&obj1);
VerifyPointer(ptr, &obj1, 1);
ptr.Reset(&obj2);
VerifyOrQuit(obj1.WasFreed());
VerifyOrQuit(!obj2.WasFreed());
VerifyPointer(ptr, &obj2, 1);
ptr.Reset();
VerifyOrQuit(obj2.WasFreed());
VerifyPointer(ptr, nullptr);
ptr.Reset(&obj3);
VerifyPointer(ptr, &obj3, 1);
}
VerifyOrQuit(obj1.WasFreed());
VerifyOrQuit(obj2.WasFreed());
VerifyOrQuit(obj3.WasFreed());
printf("\n - Self `Reset()`");
obj1.ResetTestFlags();
{
RetainPtr<TestObject> ptr1(&obj1);
RetainPtr<TestObject> ptr2;
VerifyPointer(ptr1, &obj1, 1);
ptr1.Reset(&obj1);
VerifyPointer(ptr1, &obj1, 1);
ptr2.Reset(nullptr);
VerifyPointer(ptr2, nullptr);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - Assignment `=`");
obj1.ResetTestFlags();
obj2.ResetTestFlags();
{
RetainPtr<TestObject> ptr1(&obj1);
RetainPtr<TestObject> ptr2(&obj2);
RetainPtr<TestObject> ptr3;
VerifyPointer(ptr1, &obj1, 1);
VerifyPointer(ptr2, &obj2, 1);
VerifyPointer(ptr3, nullptr);
// Set from non-null (ptr1) to non-null (ptr2)
ptr2 = ptr1;
VerifyPointer(ptr1, &obj1, 2);
VerifyPointer(ptr2, &obj1, 2);
VerifyOrQuit(obj2.WasFreed());
// Set from null (ptr3) to non-null (ptr1)
ptr1 = ptr3;
VerifyPointer(ptr1, nullptr);
VerifyPointer(ptr3, nullptr);
VerifyPointer(ptr2, &obj1, 1);
// Move from null (ptr1) to null (ptr3)
ptr3 = ptr1;
VerifyPointer(ptr1, nullptr);
VerifyPointer(ptr3, nullptr);
// Move from non-null (ptr2) to null (ptr3)
ptr3 = ptr2;
VerifyPointer(ptr2, &obj1, 2);
VerifyPointer(ptr3, &obj1, 2);
}
VerifyOrQuit(obj1.WasFreed());
VerifyOrQuit(obj2.WasFreed());
printf("\n - Self assignment `=`");
obj1.ResetTestFlags();
{
RetainPtr<TestObject> ptr1(&obj1);
RetainPtr<TestObject> ptr2;
VerifyPointer(ptr1, &obj1, 1);
VerifyPointer(ptr2, nullptr);
// Set from non-null (ptr1) to itself. We use `*(&ptr1) to
// silence `clang` error/warning not allowing explicit self
// assignment to same variable.
ptr1 = *(&ptr1);
VerifyPointer(ptr1, &obj1, 1);
// Set from null (ptr2) to itself
ptr2 = *(&ptr2);
VerifyPointer(ptr2, nullptr);
}
VerifyOrQuit(obj1.WasFreed());
printf("\n - `Release()` method");
obj1.ResetTestFlags();
{
RetainPtr<TestObject> ptr(&obj1);
VerifyPointer(ptr, &obj1, 1);
VerifyOrQuit(ptr.Release() == &obj1);
VerifyPointer(ptr, nullptr);
VerifyOrQuit(ptr.Release() == nullptr);
VerifyPointer(ptr, nullptr);
}
VerifyOrQuit(!obj1.WasFreed());
VerifyOrQuit(obj1.GetRetainCount() == 1);
printf("\n\n-- PASS\n");
}
} // namespace ot
int main(void)
{
ot::TestOwnedPtr();
ot::TestRetainPtr();
printf("\nAll tests passed.\n");
return 0;
}