views:

935

answers:

4

When I use static variables in C++, I often end up wanting to initialize one variable passing another to its constructor. In other words, I want to create static instances that depend on each other.

Within a single .cpp or .h file this is not a problem: the instances will be created in the order they are declared. However, when you want to initialize a static instance with an instance in another compilation unit, the order seems impossible to specify. The result is that, depending on the weather, it can happen that the instance that depends on another is constructed, and only afterwards the other instance is constructed. The result is that the first instance is initialized incorrectly.

Does anyone know how to ensure that static objects are created in the correct order? I have searched a long time for a solution, trying all of them (including the Schwarz Counter solution), but I begin to doubt there is one that really works.

One possibility is the trick with the static function member:

Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}

Indeed, this does work. Regrettably, you have to write globalObject().MemberFunction(), instead of globalObject.MemberFunction(), resulting in somewhat confusing and inelegant client code.

Update: Thank you for your reactions. Regrettably, it indeed seems like I have answered my own question. I guess I'll have to learn to live with it...

+16  A: 

You have answered your own question. Static initialization order is undefined, and the most elegant way around it (while still doing static initialization i.e. not refactoring it away completely) is to wrap the initialization in a function.

Read the C++ FAQ lite items starting from http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12

laalto
But alas, that solution is not thread-safe.
Charles Salvia
+5  A: 

Maybe you should reconsider whether you need so many global static variables. While they can sometimes be useful, often it's much simpler to refactor them to a smaller local scope, especially if you find that some static variables depend on others.

But you're right, there's no way to ensure a particular order of initialization, and so if your heart is set on it, keeping the initialization in a function, like you mentioned, is probably the simplest way.

Jesse Beder
You are right, it is unwise to use too much global static variables, but for some cases it avoids having to pass the same object around excessively. Think of the logger object, the container for persistent variables, the collection of all IPC connections, etc...
Dimitri C.
+2  A: 

Indeed, this does work. Regrettably, you have to write globalObject().MemberFunction(), instead of globalObject.MemberFunction(), resulting in somewhat confusing and inelegant client code.

But the most important thing is that it works, and that it is failure proof, ie. it is not easy to bypass the correct usage.

Program correctness should be your first priority. Also, IMHO, the () above is purely stylistic - ie. completely unimportant.

Depending on your platform, be careful of too much dynamic initialization. There is a relatively small amount of clean up that can take place for dynamic initializers (see here). You can solve this problem using a global object container that contains members different global objects. You therefore have:

Globals & getGlobals ()
{
  static Globals cache;
  return cache;
}

There is only one call to ~Globals() in order to clean up for all global objects in your program. In order to access a global you still have something like:

getGlobals().configuration.memberFunction ();

If you really wanted you could wrap this in a macro to save a tiny bit of typing using a macro:

#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();

Although, this is just syntactic sugar on your initial solution.

Richard Corden
A: 

Most compilers (linkers) actually do support a (non-portable) way of specifying the order. For example, with visual studio you can use the init_seg pragma to arrange the initialization into several different groups. AFAIK there is no way to guarantee order WITHIN each group. Since this is non-portable you may want to consider if you can fix your design to not require it, but the option is out there.

Dolphin