Joe
Joe

Reputation: 81

Static initialization order fiasco for built-in objects/libraries

If I have some variables that I'm initializing statically (before main begins), am I free to use any built-in stuff in these constructors, like <iostream> or <vector>?

The "static initialization order fiasco" occurs because the order in which static variables are initialized (among different translation units) is undefined.

So what if something benign like

std::cout << "Hello" << std::endl;

happens to rely on some static variable inside <iostream> being initialized ahead of time? (I'm not saying it does, but assume it did.) What's to say that these static variables inside built-in libraries are initialized before my own static variables? Like inside say "Person.cpp" or whatever.

Edit: Is std::cout guaranteed to be initialized? was suggested as a duplicate to this question. However, I think my question is slightly broader in scope because it asks about any standard built-in library, rather than just <iostream>.

Upvotes: 8

Views: 550

Answers (2)

mksteve
mksteve

Reputation: 13073

The C++ standards make no strong statements of the behavior of the program before the start of main, or after main has completed.

In experience, I have found a number of issues where the C++ runtime has destroyed some object (e.g. resources used for management of std::mutex), which have created a deadlock in the destruction of a complex type.

I would recommend the following pattern for static's

C++ creates objects declared static in a function in the order they are executed. This leaves a pattern which will ensure that objects exist as they are needed.

   AnObject * AnObject::getInstance() {
       static AnObject a;
       return &a;
   }

This should be executed to get hold of the global, and will occur at the point when getInstance() is called.

C++ 11 onwards

This code is guaranteed to be thread-safe, where if multiple threads of execution arrive in getInstance, only one will construct the object and the rest will wait.

Pre C++ 11

Creating this pattern replaces ill-defined order with thread safety issues.

Luckily, it will be possible to create a criticalsection/mutex primative in main, which is able to arbitrate.

In some OSs (e.g. InitializeCriticalSection and Windows), these locks can safely be created before main as static variables.

   AnObject * AnObject::getInstance() {
       EnterCriticalSection( &aObjectcrit );
       static AnObject a;
       LeaveCriticalSection( &aObjectcrit );
       return &a;
   }

Assuming you have initialized aObjectcrit either in or before this function is called.

The result of this pattern is a form of onion construction, where objects are required in the order they are needed, and when the program exits, they are destroyed in the reverse order they were created in.

Upvotes: 1

MSalters
MSalters

Reputation: 179819

You're confusing objects (std::cout) and types (std::vector). The former is covered by the linked question, and the latter is a type. Static initialization applies to objects, but not to types.

Upvotes: -1

Related Questions