Created
September 21, 2018 01:45
-
-
Save jroweboy/d65018f7fbaef60dcacfcd22d95a72b8 to your computer and use it in GitHub Desktop.
Trying to figure out a good way to make handle this scenario
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// place where other input state is stored | |
std::shared_ptr<SDL> state; | |
// Ties the lifetime of the SDL input code to whatever the frontend decides is important | |
void Init() { | |
state = SDL::Init(); | |
} | |
void Shutdown() { | |
state.reset(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SDL { | |
// stuff like threads that we want to determine the order of destruction | |
}; | |
std::shared_ptr<SDL> killmepls; | |
std::shared_ptr<SDL> Init() { | |
killmepls = std::make_shared<SDL>(); | |
return killmepls; | |
} | |
InputDevice::GetController() { | |
// uses `killmepls` to find what controllers have been registered | |
} | |
// several other methods in a variety of classes that all use the state in killmepls |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Assume all code implemented within the class functions inline in the classes are intended to be implemented in the cpp file
In this case you would likely pass a reference to that state, or specific bits of that state to the relevant classes and provide a helper function, if you want, to get rid of the need to explicitly specify the state. So let's step back and look at this from top down. We have a System class that drives everything, and given SDL is a subsystem, we can't place that directly in the System class, but we can make a class to encapsulate an input subsystem, so let's do that.
A very basic class one might be inclined to write is like so:
This has an issue however. Common state that can be shared between all input subsystem implementations needs to be duplicated, as the initialization and shutdown functions are pure virtual. Instead we can make this better by making the actual functions a backend needs to implement as private, and make
Initialize()
andShutdown()
non-virtual. And so it'll look something like so:Now this gives you a couple of benefits:
The actual externally usable portion of the interface does not need to change if only the virtual functions need to change. They're hidden from external code using them, and so it doesn't need to be modified if the internals get shuffled around with regards to lifetimes or init and shutdown
You now have a place for common state that isn't dependent on the subsystems directly, and so, you can place that here and expose it as necessary through functions, without having to worry about it sitting in unrelated places. This state would likely be
protected
so derived classes can see it and alter it as necessary.Now, it's very important to consider the following.
So this also creates an explicit boundary point one can see when doing review.
Alright, so we have our basic area to place stuff, now how do we create the necessary input devices and also allow it to access that common state?
Well, it's already mentioned that the common state would be protected, so that's easily accessible, for this example we'll call it
common_state
and give it the type name ofCommonState
from here on. Now the SDL specifics need to be taken care of. So let's do that. We can define the subclass like so:We use the PImpl idiom here, because ideally we want to keep all SDL specifics out of the header, since the source file that includes the header this class is defined in will be a general source file that ties everything together, and we don't want anything from SDL being included in that, as it could clash with symbols in some other hypothetical input library. So this would look something like:
Now we have places for both the common shared state, and the SDL state. Now all that remains is to modify the necessary interfaces to take the reference of either one of (or both of)
CommonState
andSDLState
and access the data. For example, if you need the SDLSpecific state in a hypotheticalSDLJoystick
class, one would do:or if you need that state to always be accessible to the class, take a reference to it in the constructor
With this approach, you have the guarantee that the overall subsystem class will outlive everything created under it, as the shutdown call in the interface should tear down everything before the subsystem class is destroyed.
For example, lets do
GetSDLJoystickByGUID()
from the codebase:We just pass in the state as a parameter. If the shared common state would ever be needed, then that can be passed as a parameter too.
Now we just need to tie it all together. In some common header/source file pair we'd have the following:
Then just use that to initialize it. Hopefully that answers the question thoroughly