RealtimeStyleTransferRuntime/Source/LyraGame/Workshop/ServiceLocator.h

205 lines
6.6 KiB
C++

// 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;
};