I ran into a weird problem today that I don’t really fully understand. Hopefully someone here can help.
The setup is fairly easy. I have a class that has a static member of type std::set. The class has 2 template constructors that differ in the number of arguments only. It’s the same behavior for both constructors, so just note that the constructor is templatized, and the constructor is searching and inserting into the std::set.
I’m experiencing the following behavior:
For a static instance of the class, the constructor crashes on the first method called on the static std::set (find()). It looks like the set is not initialized. It seems to me like the constructor is being called before the static member variable is initialized.
Here is a simplified example:
////////// Header File
class ConVar : public IListener
{
friend EventHandler; // Event Handler auto registers all instances of convar to commands
public: // Auto
template< typename T >
ConVar(string const& name, string const& description, T const& default_value );
private:
static std::set<u32> mRegisteredVars;
};
//////// INL file (included from header)
template< typename T >
ConVar::ConVar(string const& name, string const& description, T const& default_value )
: mName(name),
mhName(name),
mDescription(description),
mClamp(false)
{
u32 hname = CONSTHASH(name.c_str());
ErrorIf(mRegisteredVars.find(hname) != mRegisteredVars.end(), "Attempt to create same ConVar multiple times. Note the ConVars are static singletons!");
*this = default_value;
mRegisteredVars.insert(hname);
gCore.Events.Subscribe(mhName, this);
}
///////////// .cpp file
std::set<u32> ConVar::mRegisteredVars;
The crash occurs inside of the ErrorIf on the find method. If I comment that line it crashes on the line where it inserts.
The constructor is called before main (static instance of the class)
Does anyone know what might be going on here?
Global objects that access each other from the constructor are going to have problems with the order of their instanciation.
There are a couple of ways around this:
Try
Then wherever you use:
mRegisteredVarschange togetRegisteredVarsSet()Now even if you access
mRegisteredVarsfrom the constructor of a static storage duration object the call getRegisteredVarsSet() (to retrieve it) will guarantee that mRegisteredVars will be fully initialized before it is returned and thus available for use.Because it is a static member of the function its lifespan is the length of the program thus it will retain its state between calls.