implemented some traits for demonstration
This commit is contained in:
parent
925fab028c
commit
7eb843d6c2
|
@ -1,4 +1,8 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInterface_005FAssetUserData_002Egenerated_002Eh_002Fl_003A_002E_002E_003FUnrealEngine_003FEngine_003FIntermediate_003FBuild_003FWin64_003FUnrealEditor_003FInc_003FEngine_003FUHT_003FInterface_005FAssetUserData_002Egenerated_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Atuple_002Fl_003AC_0021_003FProgram_0020Files_0020_0028x86_0029_003FMicrosoft_0020Visual_0020Studio_003F2019_003FCommunity_003FVC_003FTools_003FMSVC_003F14_002E29_002E30133_003Finclude_003Ftuple_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Atuple_002Fl_003AC_0021_003FProgram_0020Files_003FMicrosoft_0020Visual_0020Studio_003F2022_003FCommunity_003FVC_003FTools_003FMSVC_003F14_002E31_002E31103_003FINCLUDE_003Ftuple_002Fz_003A2_002D0/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=DBEB7B51_002D847C_002D3722_002D8A0A_002DDC86C4CBFFF7_002Fdl_003ASource_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUnrealEngine_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003AEngine_002Fd_003APrivate_002Ff_003AUnrealEngine_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d2127dab_002D4e38_002D44c9_002Dabb4_002D0bc433f56925/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Session" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d2127dab_002D4e38_002D44c9_002Dabb4_002D0bc433f56925/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Session" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
<Nothing />
|
<Nothing />
|
||||||
</SessionState></s:String></wpf:ResourceDictionary>
|
</SessionState></s:String></wpf:ResourceDictionary>
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include "PerWorldServiceLocator.h"
|
||||||
|
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
|
||||||
|
FServiceLocator* UPerWorldServiceLocator::Get(UObject* WorldContextObject)
|
||||||
|
{
|
||||||
|
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
|
||||||
|
if(!World)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
UPerWorldServiceLocator* PerWorldServiceLocator = World->GetSubsystem<UPerWorldServiceLocator>();
|
||||||
|
if(!PerWorldServiceLocator)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &PerWorldServiceLocator->GetServiceLocator();
|
||||||
|
}
|
||||||
|
|
||||||
|
FServiceLocator& UPerWorldServiceLocator::GetServiceLocator()
|
||||||
|
{
|
||||||
|
return ServiceLocator;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "ServiceLocator.h"
|
||||||
|
#include "Subsystems/WorldSubsystem.h"
|
||||||
|
|
||||||
|
#include "PerWorldServiceLocator.generated.h"
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class LYRAGAME_API UPerWorldServiceLocator : public UWorldSubsystem
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
static FServiceLocator* Get(UObject* WorldContextObject);
|
||||||
|
|
||||||
|
FServiceLocator& GetServiceLocator();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FServiceLocator ServiceLocator;
|
||||||
|
};
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Aesir Interactive GmbH, (c) 2019
|
||||||
|
|
||||||
|
#include "ServiceLocator.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if WITH_DEV_AUTOMATION_TESTS
|
||||||
|
|
||||||
|
#include "Components/ActorComponent.h"
|
||||||
|
#include "Components/SceneComponent.h"
|
||||||
|
|
||||||
|
static_assert(TModels<CHasStaticUClass, UActorComponent>::Value, "Actor component should have a static uclass");
|
||||||
|
static_assert(TModels<CHasStaticUClass, UObject>::Value);
|
||||||
|
static_assert(!TModels<CHasStaticUClass, IInterface_AssetUserData>::Value);
|
||||||
|
|
||||||
|
static_assert(TModels<CHasInterfaceUClass, IInterface_AssetUserData>::Value);
|
||||||
|
static_assert(TIsUInterface<IInterface_AssetUserData>::Value);
|
||||||
|
static_assert(TModels<CHasInterfaceUClass,UActorComponent>::Value);
|
||||||
|
static_assert(!TModels<CHasInterfaceUClass, UObject>::Value);
|
||||||
|
static_assert(!TIsUInterface<UActorComponent>::Value);
|
||||||
|
static_assert(!TIsUInterface<UObject>::Value);
|
||||||
|
|
||||||
|
|
||||||
|
class FServiceUser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
USceneComponent* SceneComponent = nullptr;
|
||||||
|
UActorComponent* ActorComponent = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
BEGIN_DEFINE_SPEC(TServiceLocatorSpec, "Workshop.ServiceLocator", EAutomationTestFlags::ProductFilter | EAutomationTestFlags::ApplicationContextMask)
|
||||||
|
FServiceUser ServiceUser;
|
||||||
|
FServiceLocator ServiceLocator;
|
||||||
|
USceneComponent* SceneComponent = nullptr;
|
||||||
|
UActorComponent* ActorComponent = nullptr;
|
||||||
|
END_DEFINE_SPEC(TServiceLocatorSpec)
|
||||||
|
|
||||||
|
|
||||||
|
void TServiceLocatorSpec::Define()
|
||||||
|
{
|
||||||
|
BeforeEach([this]()
|
||||||
|
{
|
||||||
|
SceneComponent = NewObject<USceneComponent>();
|
||||||
|
ActorComponent = NewObject<UActorComponent>();
|
||||||
|
});
|
||||||
|
|
||||||
|
Describe("ProvideService", [this]()
|
||||||
|
{
|
||||||
|
It("Should work for UObjects", [this]()
|
||||||
|
{
|
||||||
|
TestNull("GetService<UActorComponent>()", ServiceLocator.GetService<UActorComponent>());
|
||||||
|
ServiceLocator.ProvideService<UActorComponent>(ActorComponent);
|
||||||
|
|
||||||
|
TestEqual("GetService<UActorComponent>()", ServiceLocator.GetService<UActorComponent>(), ActorComponent);
|
||||||
|
});
|
||||||
|
|
||||||
|
It("Should work for UInterfaces", [this]()
|
||||||
|
{
|
||||||
|
TestNull("GetService<UActorComponent>()", ServiceLocator.GetService<IInterface_AssetUserData>().GetInterface());
|
||||||
|
ServiceLocator.ProvideService<IInterface_AssetUserData>(ActorComponent);
|
||||||
|
|
||||||
|
TestEqual("GetService<UInterface_AssetUserData>()",
|
||||||
|
ServiceLocator.GetService<IInterface_AssetUserData>().GetInterface(),
|
||||||
|
static_cast<IInterface_AssetUserData*>(ActorComponent));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Describe("GetServices", [this]()
|
||||||
|
{
|
||||||
|
BeforeEach([this]()
|
||||||
|
{
|
||||||
|
ServiceLocator.ProvideService<UActorComponent>(ActorComponent);
|
||||||
|
ServiceLocator.ProvideService<USceneComponent>(SceneComponent);
|
||||||
|
ServiceLocator.ProvideService<IInterface_AssetUserData>(ActorComponent);
|
||||||
|
});
|
||||||
|
It("Should get all the services", [this]()
|
||||||
|
{
|
||||||
|
auto [InjectedActorComponent, InjectedSceneComponent, InjectedInterface] = ServiceLocator.GetServices<UActorComponent, USceneComponent, IInterface_AssetUserData>();
|
||||||
|
TestEqual(TEXT("InjectedActorComponent"), InjectedActorComponent, ActorComponent);
|
||||||
|
TestEqual(TEXT("InjectedSceneComponent"), InjectedSceneComponent, SceneComponent);
|
||||||
|
TestEqual(TEXT("InjectedInterface"), InjectedInterface.GetInterface(), static_cast<IInterface_AssetUserData*>(ActorComponent));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,204 @@
|
||||||
|
// Aesir Interactive GmbH, (c) 2019
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Templates/Casts.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
namespace TIsUBaseInterfacePrivate
|
||||||
|
{
|
||||||
|
template<class InterfaceClass>
|
||||||
|
struct TIsUBaseInterfaceImpl;
|
||||||
|
template<>
|
||||||
|
struct TIsUBaseInterfaceImpl<bool> { enum { Value = false };};
|
||||||
|
template<>
|
||||||
|
struct TIsUBaseInterfaceImpl<UClass*> { enum { Value = true };};
|
||||||
|
}
|
||||||
|
template<class InterfaceClass>
|
||||||
|
struct TIsUBaseInterface { enum { Value = TIsUBaseInterfacePrivate::TIsUBaseInterfaceImpl<decltype(&InterfaceClass::StaticClass, true)>::Value}; };
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct CHasStaticUClass
|
||||||
|
{
|
||||||
|
template<class ObjectClass>
|
||||||
|
auto Requires() -> decltype(
|
||||||
|
ObjectClass::StaticClass()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CHasInterfaceUClass
|
||||||
|
{
|
||||||
|
template<class InterfaceClass>
|
||||||
|
auto Requires() -> decltype(
|
||||||
|
InterfaceClass::UClassType::StaticClass()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class InterfaceClass>
|
||||||
|
struct TIsUInterface
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Value = !TModels<CHasStaticUClass, InterfaceClass>::Value && TModels<CHasInterfaceUClass, InterfaceClass>::Value
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General purpose service locator that is available from the world settings
|
||||||
|
*/
|
||||||
|
class LYRAGAME_API FServiceLocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DECLARE_EVENT_OneParam(FServiceLocator, FServiceRegisteredEvent, FServiceLocator&);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the registered service cast to ServiceClass or nullptr if the service is not registered or the cast failed
|
||||||
|
*/
|
||||||
|
template <class ServiceClass>
|
||||||
|
bool IsServiceProvided() const
|
||||||
|
{
|
||||||
|
const UClass* ServiceUClass = GetServiceUClass<ServiceClass>();
|
||||||
|
return Services.Contains(ServiceUClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the registered service cast to ServiceClass or nullptr if the service is not registered or the cast failed
|
||||||
|
*/
|
||||||
|
template <class ServiceType>
|
||||||
|
typename TEnableIf<!TIsIInterface<ServiceType>::Value, ServiceType*>::Type
|
||||||
|
GetService() const
|
||||||
|
{
|
||||||
|
const UClass* ServiceUClass = GetServiceUClass<ServiceType>();
|
||||||
|
UObject* const* ServiceInstance = Services.Find(ServiceUClass);
|
||||||
|
if (!ServiceInstance)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return Cast<ServiceType>(*ServiceInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the registered service as TScriptInterface or nullptr if the service is not registered or the cast failed
|
||||||
|
*/
|
||||||
|
template<class ServiceInterface>
|
||||||
|
typename TEnableIf<TIsIInterface<ServiceInterface>::Value, TScriptInterface<ServiceInterface>>::Type
|
||||||
|
GetService() const
|
||||||
|
{
|
||||||
|
const UClass* ServiceUClass = GetServiceUClass<ServiceInterface>();
|
||||||
|
UObject* const* ServiceInstance = Services.Find(ServiceUClass);
|
||||||
|
if (!ServiceInstance)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return TScriptInterface<ServiceInterface>(&**ServiceInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the registered service cast to ServiceClass. Crashes if the service is not available.
|
||||||
|
*/
|
||||||
|
template <class ServiceClass>
|
||||||
|
typename TEnableIf<!TIsIInterface<ServiceClass>::Value, ServiceClass*>::Type
|
||||||
|
GetServiceChecked() const
|
||||||
|
{
|
||||||
|
const UClass* ServiceUClass = GetServiceUClass<ServiceClass>();
|
||||||
|
|
||||||
|
UObject *const* ServiceObject = Services.Find(ServiceUClass);
|
||||||
|
checkf(ServiceObject, TEXT("No object was found for service %s"), *ServiceUClass->GetName());
|
||||||
|
UObject *const ServiceCast = Cast<ServiceClass>(*ServiceObject);
|
||||||
|
checkf(IsValid(ServiceCast), TEXT("Object %s is not of type %s"), *((*ServiceObject)->GetName()), *ServiceUClass->GetName());
|
||||||
|
return Cast<ServiceClass>(*ServiceObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the registered service cast to ServiceClass. Crashes if the service is not available.
|
||||||
|
*/
|
||||||
|
template<class ServiceInterface>
|
||||||
|
typename TEnableIf<TIsIInterface<ServiceInterface>::Value, TScriptInterface<ServiceInterface>>::Type
|
||||||
|
GetServiceChecked() const
|
||||||
|
{
|
||||||
|
const UClass* ServiceUClass = GetServiceUClass<ServiceInterface>();
|
||||||
|
|
||||||
|
UObject *const* ServiceObject = Services.Find(ServiceUClass);
|
||||||
|
checkf(ServiceObject, TEXT("No object was found for service %s"), *ServiceUClass->GetName());
|
||||||
|
checkf(Cast<ServiceInterface>(ServiceObject), TEXT("Object %s does not implement %s"), *(*ServiceObject)->GetName(), *ServiceUClass->GetName());
|
||||||
|
return TScriptInterface<ServiceInterface>(*ServiceObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given object interface with the service locator.
|
||||||
|
* Will refuse to work if the type is not actually derived from the interface.
|
||||||
|
* Note: you should always explicitly provide the template list to make sure you register the service as the intended type
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
template <class ServiceInterface, class ChildType>
|
||||||
|
typename TEnableIf<TIsDerivedFrom<ChildType, ServiceInterface>::Value, void>::Type
|
||||||
|
ProvideService(ChildType* ServiceInstance)
|
||||||
|
{
|
||||||
|
const UClass* ServiceUClass = GetServiceUClass<ServiceInterface>();
|
||||||
|
Services.Emplace(ServiceUClass, ServiceInstance);
|
||||||
|
NotifyServiceProvided(ServiceUClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the given object with the service locator
|
||||||
|
* Note: you should always explicitly provide the template list to make sure you unregister the service as the intended type
|
||||||
|
*/
|
||||||
|
template<class ServiceClass>
|
||||||
|
void WithdrawService(ServiceClass* ServiceInstance)
|
||||||
|
{
|
||||||
|
const UClass* ServiceClassObject = GetServiceUClass<ServiceClass>();
|
||||||
|
ensureMsgf(GetService<ServiceClass>() == ServiceInstance, TEXT("The service you are trying to unregister is not the one that has been provided!"));
|
||||||
|
Services.Remove(ServiceClassObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Waits for the service class to be registered or calls the callback immediately if the service is already registerd */
|
||||||
|
template<class ServiceClass>
|
||||||
|
void WaitForService(FServiceRegisteredEvent::FDelegate Callback)
|
||||||
|
{
|
||||||
|
const UClass* ServiceUClass = GetServiceUClass<ServiceClass>();
|
||||||
|
ServiceRegisteredCallbacks.FindOrAdd(ServiceUClass).Add(Callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ServiceType>
|
||||||
|
struct ServiceTypeMapper
|
||||||
|
{
|
||||||
|
using MappedType = decltype(DeclVal<FServiceLocator>().GetService<ServiceType>());
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename...ServiceTypes>
|
||||||
|
auto GetServices() -> decltype(auto)
|
||||||
|
{
|
||||||
|
return MakeTuple<typename ServiceTypeMapper<ServiceTypes>::MappedType...>(GetService<ServiceTypes>()...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void NotifyServiceProvided(const UClass* ServiceUClass)
|
||||||
|
{
|
||||||
|
FServiceRegisteredEvent& ServiceRegisteredEvent = ServiceRegisteredCallbacks.FindOrAdd(ServiceUClass);
|
||||||
|
ServiceRegisteredEvent.Broadcast(*this);
|
||||||
|
ServiceRegisteredEvent.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This statically gets the UClass from any given IInterface-Type */
|
||||||
|
template <typename ServiceInterface>
|
||||||
|
static typename TEnableIf<TIsUInterface<ServiceInterface>::Value,const UClass*>::Type
|
||||||
|
GetServiceUClass()
|
||||||
|
{
|
||||||
|
return ServiceInterface::UClassType::StaticClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This statically gets the UClass from any given UObject-Type */
|
||||||
|
template <typename ServiceClass>
|
||||||
|
static typename TEnableIf<TModels<CHasStaticUClass, ServiceClass>::Value,const UClass*>::Type
|
||||||
|
GetServiceUClass()
|
||||||
|
{
|
||||||
|
return ServiceClass::StaticClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
TMap<const UClass*, FServiceRegisteredEvent> ServiceRegisteredCallbacks;
|
||||||
|
|
||||||
|
TMap<const UClass*, UObject*> Services;
|
||||||
|
};
|
Loading…
Reference in New Issue