Added implicit conversion and tests
This commit is contained in:
parent
7d022bae76
commit
925fab028c
|
@ -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
|
|
@ -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;
|
||||||
|
};
|
Loading…
Reference in New Issue