In a small game project I'm using 'Init' and 'Shutdown' functions on Systems/Managers/Components to make resetting them easier without having to call the constructor/destructor.
For example when reloading a level you can just call Shutdown and Init to reset to the starting state.
A potential problem however I noticed is that I have to be careful when I call Init and Shutdown, and unless I track the init, or 'active', state of the object and explicitly check for it, undefined behavior could creep in. For example, if I ever accidentally shutdown without init first, etc.
If for no reason other than curiosity, I wanted to see if I could make an abstract interface that would make tracking and maintaining the active state easier. It would be included in any class that uses the 'Init' 'Shutdown' format and with minimal other work from the user would help prevent some of the potential issues. Here is the behavior I wanted to achieve:
- Calling Init when not active makes the object active
- Calling Shutdown when active makes the object inactive
- Calling Shutdown when inactive is ignored
- Calling Init when already active calls Shutdown, then Init.
The interface I came up with is as follows:
class InitInterface
{
public:
InitInterface() : IsActive(IsActive_), IsActive_(false) {}
// 1. If we are not active, set us to active
// 2. If we are active, run shutdown (virtually)
void PreInit()
{
if(IsActive) this->Shutdown(); // virtual shutdown call
IsActive_ = true;
}
// return value represents if a shutdown is necessary given our current active state.
// 1. If we are not active then we can't shut down, so return true (skip shutting down in the derived class)
// 2. If we are active, set us to not active and return false (we do need to shut down and not skip it in the derived class)
bool PreShutdown()
{
if(!IsActive) return true;
IsActive_ = false;
return false;
}
// pure virtual functions we need to implement
virtual void Init() = 0;
virtual void Shutdown() = 0;
const bool &IsActive; // read-only
private:
bool IsActive_;
};
And to use it the minimum user implementation I've come up with would be:
class B : public A, public InitInterface
{
public:
// REQUIRED 1: Shutdown in Destructor
~B() { Shutdown(); }
// REQUIRED 2: Implement Init and call PreInit() as first line
void Init()
{
PreInit(); // calls shutdown virtually
// INIT CODE HERE
}
// REQUIRED 3: Implement Shutdown and check if PreShutdown() indicates a shutdown is necessary
void Shutdown()
{
if(PreShutdown()) return;
// SHUT DOWN CODE HERE
}
};
Basically when the user writes a class that should support Init/Shutdown, they include it as a base class and must include the three sections marked as REQUIRED
.
Here's an example program to test.
My question is, Is there any way to simplify what the user is required to write even further?
For example I initially wanted to virtually call Shutdown in the destructor of InitInterface
to eliminate requirement #1, but realized that this wouldn't work since class B would be destructed before InitInterface.