We cannot determine the order of the initialization of static objects.
But is this a problem in the following example?
the code:
class Factory
{
public:
static bool Register(name, func);
private:
static map<string, func> s_map;
};
// in cpp file
map<string, func> Factory::s_map;
bool Factory::Register(name, func)
{
s_map[name] = func;
}
and in another cpp file
static bool registered = Factory::Register("myType", MyTypeCreate);
When I register more types I don't depend on the order in the container. But what about the first addition to the container? Can I be sure it's initialized "enough" to take the first element?
Or it's another problem of "static initialization order fiasco"?
Your scenario is not guaranteed to work as expected. The success depends on the link order.
One approach to be sure is to access the map through a (static) function that creates the object as a static variable like this:
class Factory
{
public:
static bool Register(name, func);
private:
static map<string, func>& TheMap();
};
map<string, func>& Factory::TheMap()
{
static map<string, func> g_;
return g_;
}
bool Factory::Register(name, func)
{
TheMap()[name] = func;
}
The downside of this is that the order of destruction of static variables is hard to control by you as the developer. In the case of the map this is no problem. But if static variables reference each other, the "static linking fiasco" gets even worse: In my experience it's much harder to prevent/debug a crash when a program ends compared to when it starts.
Being lazy, here's a copy from http://en.cppreference.com/:
Non-local variables
All non-local variables with static storage duration are initialized as part of program startup, before the execution of the main function begins (unless deferred, see below).
...
Dynamic initialization
After all static initialization is completed, dynamic initialization of non-local variables occurs in the following situations:
...
Deferred dynamic initialization
It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.
If the initialization of a non-inline variable is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized.
The important part is odr-use:
ODR-use
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it;
Since the s_map
is populated through Factory::Register
, I don't see a problem here.
If the map implementation is very trivial, it may even be initialized as part of the static initialization/at compile time.
But even if the initialization is deferred, it will be initialized before the use in Factory::Register
, as long as both are in the same translation unit.
However, if the map is defined in one translation unit, and Factory::Register
is defined in another, anything can happen.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With