Added implicit conversion and tests

This commit is contained in:
Manuel Wagner 2022-05-23 20:53:52 +02:00
parent 7d022bae76
commit 925fab028c
2 changed files with 168 additions and 0 deletions

View File

@ -0,0 +1,104 @@
#include "RootedObject.h"
#if WITH_DEV_AUTOMATION_TESTS
template<typename AssigneeType, typename ValueType>
struct TIsAssignableFrom
{
struct TrueType {};
template<typename T>
static T DeclVal(); // intentionally not implemented
enum
{
Value = TIsSame<decltype(DeclVal<AssigneeType>() = DeclVal<ValueType>(), DeclVal<TrueType>()), TrueType>::Value
};
};
static_assert(TIsConstructible<TRootedObject<UObject>>::Value, "TRootedObject should be default constructible");
static_assert(TIsConstructible<TRootedObject<UObject>, TRootedObject<UObject>&&>::Value, "TRootedObject should be move constructible");
static_assert(TIsAssignableFrom<TRootedObject<UObject>, TRootedObject<UObject>&&>::Value, "TRootedObject should be move assignable");
static_assert(TIsAssignableFrom<UObject*&, TRootedObject<UObject>>::Value, "TRootedObject should implicitly castable to its inner type");
BEGIN_DEFINE_SPEC(TRootedObjectSpec, "Workshop.RootedObject", EAutomationTestFlags::ProductFilter | EAutomationTestFlags::ApplicationContextMask)
UObject* TheInnerObject;
END_DEFINE_SPEC(TRootedObjectSpec)
void TRootedObjectSpec::Define()
{
BeforeEach([this]()
{
TheInnerObject = NewObject<USceneComponent>();
});
Describe("Construct", [this]()
{
It("Should add the rooted object to the root", [this]()
{
TRootedObject<UObject> RootedObject(TheInnerObject);
TestTrue("TheInnerObject->IsRooted()", TheInnerObject->IsRooted());
});
It("Should move correctly", [this]()
{
TRootedObject<UObject> RootedObject(TheInnerObject);
TRootedObject<UObject> OtherRootedObject;
OtherRootedObject = MoveTemp(RootedObject);
TestTrue("TheInnerObject->IsRooted()", TheInnerObject->IsRooted());
TestFalse("RootedObject.IsValid()", RootedObject.IsValid());
TestTrue("OtherRootedObject.IsValid()", OtherRootedObject.IsValid());
});
});
Describe("Destruct", [this]()
{
It("Should remove the rooted object from the root", [this]()
{
{
TRootedObject<UObject> RootedObject(TheInnerObject);
TestTrue("TheInnerObject->IsRooted()", TheInnerObject->IsRooted());
}
TestFalse("TheInnerObject->IsRooted()", TheInnerObject->IsRooted());
});
});
Describe("RetrieveObject", [this]()
{
It("Should invalidate the rooted object", [this]()
{
TRootedObject<UObject> RootedObject(TheInnerObject);
RootedObject.RetrieveObject();
TestFalse("RootedObject.IsValid()", RootedObject.IsValid());
});
It("Should return the inner object", [this]()
{
TRootedObject<UObject> RootedObject(TheInnerObject);
UObject* RetrievedObject = RootedObject.RetrieveObject();
TestEqual("RootedObject.IsValid()", TheInnerObject, RetrievedObject);
});
It("Should remove the inner object from root", [this]()
{
TRootedObject<UObject> RootedObject(TheInnerObject);
UObject* RetrievedObject = RootedObject.RetrieveObject();
TestFalse("RetrievedObject->IsRooted()", RetrievedObject->IsRooted());
});
It("Implicit Retrieve is same as explicit retrieve", [this]()
{
TRootedObject<UObject> RootedObject(TheInnerObject);
UObject* RetrievedObject = RootedObject;
TestEqual("RootedObject.IsValid()", TheInnerObject, RetrievedObject);
TestFalse("RetrievedObject->IsRooted()", RetrievedObject->IsRooted());
});
});
}
#endif

View File

@ -0,0 +1,64 @@
#pragma once
#include "CoreMinimal.h"
template <class UObjectType>
class TRootedObject
{
public:
using SelfType = TRootedObject<UObjectType>;
static_assert(TIsDerivedFrom<UObjectType, UObject>::Value, "This class only works with UObjects");
TRootedObject() = default;
explicit TRootedObject(UObjectType* Object)
: OwnedObject(Object)
{
OwnedObject->AddToRoot();
}
TRootedObject(const SelfType& Other) = delete;
SelfType& operator=(const SelfType& Other) = delete;
TRootedObject(SelfType&& Other)
{
*this = Other;
}
SelfType& operator=(SelfType&& Other)
{
Swap(OwnedObject, Other.OwnedObject);
return *this;
}
~TRootedObject()
{
if (::IsValid(OwnedObject))
{
OwnedObject->RemoveFromRoot();
}
}
operator UObjectType*()
{
return RetrieveObject();
}
UObjectType* RetrieveObject()
{
UObjectType* Temp = OwnedObject;
OwnedObject = nullptr;
Temp->RemoveFromRoot();
return Temp;
}
bool IsValid() const
{
return ::IsValid(OwnedObject);
}
private:
UObjectType* OwnedObject = nullptr;
};