4

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?

1
  • We need a fully running example that exhibits the behavior otherwise we are just guessing. But it sounds like you are hitting the global order of initialization problem. Are you creating an object of type ConVar<T> in the global scope? Commented Oct 15, 2011 at 1:05

2 Answers 2

4

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

//Change
static std::set<u32> mRegisteredVars;

//Into
static std::set<u32>&  getRegisteredVarsSet()
{
    static  std::set<u32>&  mRegisteredVars;
    return mRegisteredVars;
}
// Obviously remove the `std::set<u32> ConVar::mRegisteredVars;`
// From the cpp file.

Then wherever you use: mRegisteredVars change to getRegisteredVarsSet()

Now even if you access mRegisteredVars from 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.

3

Do not access static/global variables before main is called. This is called the "Static Initialization Order Fiasco".

Your mRegisteredVars simply doesn't exit at that point. Doing what you do is undefined behavior.

1
  • Thank you for pointing this out. I wasn't aware of this fiasco, I was almost certain the compiler would recognize to initialize static members before accessing the constructor - even if the class itself is static.
    – Millianz
    Commented Oct 15, 2011 at 1:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.