I'm trying to implement a service locator design pattern into my game engine. The problem is that I can't get it working. I'm still having some trouble to understand how casts really works.
ServiceLocator.h
class ELK_TOOLS_EXPORT ServiceLocator
{
public:
template<typename ServiceType>
static void Provide(ServiceType&& p_service)
{
m_services[typeid(ServiceType).hash_code()] = p_service;
}
template<typename ServiceType>
static ServiceType& Get()
{
return static_cast<ServiceType&>(m_services[typeid(ServiceType).hash_code()]);
}
private:
static std::unordered_map<size_t, ElkAPI::IManager> m_services;
};
The idea was to store services onto the stack, so I have no pointer or anything like that in my unordered_map. The problem is that when I try to use my service provider into my engine this way :
EngineManager.cpp
void ElkGameEngine::Managers::EngineManager::Setup()
{
m_quit = false;
/* Here I provide to the service locator a new Service (That inherits from IManager) */
ServiceLocator::Provide<WindowManager>(WindowManager());
ServiceLocator::Provide<SceneManager>(SceneManager());
ServiceLocator::Provide<InputManager>(InputManager());
ServiceLocator::Provide<RenderingManager>(RenderingManager(ServiceLocator::Get<WindowManager>().GetWidth(), ServiceLocator::Get<WindowManager>().GetHeight()));
ServiceLocator::Provide<PhysicsManager>(PhysicsManager());
ElkTools::Debug::Log::Process("Engine setup completed", ElkTools::Debug::Log::LogLevel::LOG_INFO);
}
// [...]
void ElkGameEngine::Managers::EngineManager::UpdatePhysics()
{
PROFILER_SPY("EngineManager::UpdatePhysics");
/* Here I try to get the service */
ServiceLocator::Get<PhysicsManager>().ApplyPhysics();
ServiceLocator::Get<PhysicsManager>().ClearPhysicsEntities();
}
The compiler (VS2017) say me this:
Severity Code Description Project File Line Suppression State
Error C2259 'ElkAPI::IManager': cannot instantiate abstract class ElkGameEngine c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\utility 292
And the compiler output is:
2>------ Build started: Project: ElkGameEngine, Configuration: Debug x64 ------
2>EngineManager.cpp
2>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\utility(292): error C2259: 'ElkAPI::IManager': cannot instantiate abstract class
2>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\utility(292): note: due to following members:
2>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\utility(292): note: 'void ElkAPI::IManager::Setup(void)': is abstract
2>c:\users\adrie\desktop\group_2\pfa\elkengine\elkapi\include\elkapi\imanager.h(15): note: see declaration of 'ElkAPI::IManager::Setup'
2>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\utility(292): note: 'void ElkAPI::IManager::Close(void)': is abstract
2>c:\users\adrie\desktop\group_2\pfa\elkengine\elkapi\include\elkapi\imanager.h(16): note: see declaration of 'ElkAPI::IManager::Close'
2>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\unordered_map(284): note: see reference to class template instantiation 'std::pair<const _Kty,_Ty>' being compiled
2> with
2> [
2> _Kty=::size_t,
2> _Ty=ElkAPI::IManager
2> ]
2>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\unordered_map(283): note: while compiling class template member function 'ElkAPI::IManager &std::unordered_map<::size_t,ElkAPI::IManager,std::hash<_Kty>,std::equal_to<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>::operator [](unsigned __int64 &&)'
2> with
2> [
2> _Kty=::size_t,
2> _Ty=ElkAPI::IManager
2> ]
2>c:\users\adrie\desktop\group_2\pfa\elkengine\build\elkrendering\include\elktools\utils\servicelocator.h(28): note: see reference to function template instantiation 'ElkAPI::IManager &std::unordered_map<::size_t,ElkAPI::IManager,std::hash<_Kty>,std::equal_to<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>::operator [](unsigned __int64 &&)' being compiled
2> with
2> [
2> _Kty=::size_t,
2> _Ty=ElkAPI::IManager
2> ]
2>c:\users\adrie\desktop\group_2\pfa\elkengine\build\elkrendering\include\elktools\utils\servicelocator.h(28): note: see reference to class template instantiation 'std::unordered_map<::size_t,ElkAPI::IManager,std::hash<_Kty>,std::equal_to<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>' being compiled
2> with
2> [
2> _Kty=::size_t,
2> _Ty=ElkAPI::IManager
2> ]
2>c:\users\adrie\desktop\group_2\pfa\elkengine\elkgameengine\src\managers\enginemanager.cpp(22): note: see reference to function template instantiation 'ServiceType &ElkTools::Utils::ServiceLocator::Get<ElkGameEngine::Managers::WindowManager>(void)' being compiled
2> with
2> [
2> ServiceType=ElkGameEngine::Managers::WindowManager
2> ]
2>Done building project "ElkGameEngine.vcxproj" -- FAILED.
========== Build: 1 succeeded, 1 failed, 2 up-to-date, 0 skipped ==========
I can't figure out why it says that IManager is an abstract class and get instancied (Said in the error list). I'm not trying to instantiate any IManager, I only instantiate my managers (That implements IManager). My managers are good, I was using them before trying to implement this Service Locator design pattern.
- Do you have any idea why I get this error ?
- Is it due to my cast (I'm trying to cast something onto the stack)
Note: I tried to comment out the provide lines in EngineManager.cpp and I still get this error, so I probably can say that the problem come from the Get method of my ServiceLocator.
I assume ElkAPI::IManager is in fact an abstract class, the I... stands for "Interface" and these are often abstract.
You are trying to maintain a c++ container class (map) of these. C++ container classes store instances by default. So if you add something to the map it tries to construct an instance and copy or move the content of the source into it. This is not possible as the map element type is abstract - thus error!
Having a map of polymorphic objects is only possible if you use a map of references or pointers to a base type and lifetime-manage the pointed to objects by other means.
You must realize a fundamental difference of c++ to most other languages which is that classes are value-types like structs and thus a variable or member of class type is copied/moved when assigned. If you want to use references only you must use pointers or references and specify so with * or &