Background:
I have a game engine where there are several extern
ed global pointers to various subsystems for ease-of-access for downstream game applications:
#pragma once
#include <type_traits>
class JobSystem;
class FileLogger;
class Renderer;
class Console;
class Config;
class UISystem;
class InputSystem;
class AudioSystem;
class EngineSubsystem;
class GameBase;
extern JobSystem* g_theJobSystem;
extern FileLogger* g_theFileLogger;
extern Renderer* g_theRenderer;
extern Console* g_theConsole;
extern Config* g_theConfig;
extern UISystem* g_theUISystem;
extern InputSystem* g_theInputSystem;
extern AudioSystem* g_theAudioSystem;
extern GameBase* g_theGame;
extern EngineSubsystem* g_theSubsystemHead;
template<typename GameDerived>
GameDerived* GetGameAs() noexcept {
static_assert(std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<std::remove_pointer_t<GameBase>>>, std::remove_cv_t<std::remove_reference_t<std::remove_pointer_t<GameDerived>>>>);
return dynamic_cast<GameDerived*>(g_theGame);
}
Problem:
I'd like to be able to provide a global pointer to the App
class but the fact that the class is a template is making it difficult to understand how to implement it.
Pseudo-code Implementation:
Class hierarchy structure:
#pragma once
#include <type_traits>
class GameBase { /*...*/ }
class Game : public GameBase { /*...*/ }
template<typename GameType>
class App {
static_assert(std::is_base_of_v<GameBase, GameType>, "Template type GameType not derived from GameBase.");
public:
static void CreateApp() {
if(_theApp) {
return;
}
_theApp = new App<T>();
}
static void DestroyApp() {
if(!_theApp) {
return;
}
delete _theApp;
_theApp = nullptr;
}
//...
private:
void SetupSubsystemPointers() {
//...
_theRenderer = std::make_unique<Renderer>();
g_theRenderer = _theRenderer.get();
//...
_theGame = std::make_unique<GameType>();
g_theGame = _theGame.get();
//...This does not work:
//g_theApp = this;
}
std::unique_ptr<JobSystem> _theJobSystem{};
std::unique_ptr<FileLogger> _theFileLogger{};
std::unique_ptr<Config> _theConfig{};
std::unique_ptr<Renderer> _theRenderer{};
std::unique_ptr<Console> _theConsole{};
std::unique_ptr<InputSystem> _theInputSystem{};
std::unique_ptr<UISystem> _theUI{};
std::unique_ptr<AudioSystem> _theAudioSystem{};
std::unique_ptr<GameType> _theGame{};
//I would really like this to be a std::unique_ptr.
static inline App<GameType>* _theApp{};
}
Here's where I'm running in to trouble implementing the global pointer to the App class.
Common header:
#pragma once
#include <type_traits>
class JobSystem;
class FileLogger;
class Renderer;
class Console;
class Config;
class UISystem;
class InputSystem;
class AudioSystem;
class EngineSubsystem;
class GameBase;
extern JobSystem* g_theJobSystem;
extern FileLogger* g_theFileLogger;
extern Renderer* g_theRenderer;
extern Console* g_theConsole;
extern Config* g_theConfig;
extern UISystem* g_theUISystem;
extern InputSystem* g_theInputSystem;
extern AudioSystem* g_theAudioSystem;
extern GameBase* g_theGame;
// WHAT DO I NEED TO DO TO GET SOMETHING LIKE THE FOLLOWING TO WORK?
//extern App* g_theApp;
//I tried
//template<typename GameType>
//App<GameType>* g_theApp but compilation failed with an undefined variable error or template vomit errors.
//extern GameType* g_theGame; //This would be nice to have. It would replace the above GameBase pointer and the below GetGameAs function
extern EngineSubsystem* g_theSubsystemHead;
template<typename GameDerived>
GameDerived* GetGameAs() noexcept {
static_assert(std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<std::remove_pointer_t<GameBase>>>, std::remove_cv_t<std::remove_reference_t<std::remove_pointer_t<GameDerived>>>>);
return dynamic_cast<GameDerived*>(g_theGame);
}
Common implementation:
#include "Engine/Core/EngineCommon.hpp"
JobSystem* g_theJobSystem = nullptr;
FileLogger* g_theFileLogger = nullptr;
Renderer* g_theRenderer = nullptr;
Console* g_theConsole = nullptr;
Config* g_theConfig = nullptr;
UISystem* g_theUISystem = nullptr;
InputSystem* g_theInputSystem = nullptr;
AudioSystem* g_theAudioSystem = nullptr;
GameBase* g_theGame = nullptr;
EngineSubsystem* g_theSubsystemHead = nullptr;
//DOES NOT WORK
//App* g_theApp = nullptr;
template<typename GameType> App<GameType>* g_theApp;
should work for you. It looks like you forgot to forward declare App in the common header, which would be the reason you can't declare a pointer to it.
Also, if you can use C++17, you can stop using extern
and start using inline
on all of your variable declarations and turn them into definitions like
inline JobSystem* g_theJobSystem = nullptr;
inline FileLogger* g_theFileLogger = nullptr;
inline Renderer* g_theRenderer = nullptr;
inline Console* g_theConsole = nullptr;
inline Config* g_theConfig = nullptr;
inline UISystem* g_theUISystem = nullptr;
inline InputSystem* g_theInputSystem = nullptr;
inline AudioSystem* g_theAudioSystem = nullptr;
inline GameBase* g_theGame = nullptr;
inline EngineSubsystem* g_theSubsystemHead = nullptr;
inline
will tell the compiler it is okay to have the variable defined in multiple translation units and the linker will merge them all into one just like it does for inline functions.