C++ static initialization order

56,492

Solution 1

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 items starting from https://isocpp.org/wiki/faq/ctors#static-init-order

Solution 2

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.

Solution 3

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.

Solution 4

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.

Solution 5

dispite the age of this thread, I would like to propose the solution I've found. As many have pointed out before of me, C++ doesn't provide any mechanism for static initialization ordering. What I propose is to encapsule each static member inside a static method of the class that in turn initialize the member and provide an access in an object-oriented fashion. Let me give you an example, supposing we want to define the class named "Math" which, among the other members, contains "PI":

class Math {
public:
   static const float Pi() {
       static const float s_PI = 3.14f;
       return s_PI;
   }
}

s_PI will be initialized the first time Pi() method is invoked (in GCC). Be aware: the local objects with static storage have an implementation dependent lifecyle, for further detail check 6.7.4 in 2.

Static keyword, C++ Standard

Share:
56,492
Popplars
Author by

Popplars

I work as a C++ developer at a Belgian IT company, writing server software. My blog can be found here.

Updated on July 05, 2022

Comments

  • Popplars
    Popplars almost 2 years

    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...

  • Popplars
    Popplars about 15 years
    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...
  • enobayram
    enobayram about 12 years
    @CharlesSalvia Bringing you back 2.5 years in time :), Is it really a problem that this is not thread-safe? We're talking about static initialisation, so, this is all happening before main(). Should we really be concerned about thread safety issues before main()?
  • José
    José about 12 years
    not before main enter, but when the method is first called. see blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx
  • Steve Jessop
    Steve Jessop over 10 years
    "Avoids passing an object excessively" is just a way of saying, "the dependencies between the components of my program are so extensive that tracking them is excessive work. So it's better to stop tracking them". Except that usually it isn't better -- if the dependencies cannot be simplified then that's when it's most useful to be able to track them down by following where the object is passed.
  • AndyG
    AndyG over 10 years
    The top answer suggests exactly this, and it was answered nearly 5 years ago. Perhaps we should move this into the top answer as an example.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 10 years
    @enobayram: Bringing you back 2.1 years in time :), Yes, it's a problem. The whole point of moving the initialisation into a function is that the initialisation now occurs not before main, but when the function is first invoked, which may be at any point during the program's execution... and in any thread.
  • a.peganz
    a.peganz about 10 years
    @Lightness, Jose, Charles: I really think your comments should be incorporated in the accepted answer. With multithreading being commonplace today this can cause nasty surprises and isn't mentioned in any of the top search results for the static initialization fiasco. i.e. parashift doesn't mention it at all.
  • Luke Worth
    Luke Worth almost 10 years
    This is not a problem in C++11. See stackoverflow.com/questions/8102125/…
  • Luke Worth
    Luke Worth almost 10 years
    Thread-safety isn't a problem in C++11. See stackoverflow.com/questions/8102125/…
  • einpoklum
    einpoklum over 8 years
    How is this different fro, OP's approach, except that you made the function a member of a class?
  • underscore_d
    underscore_d about 8 years
    also not an illuminating example, since an object like that can and should be constexpr
  • SimplyKnownAsG
    SimplyKnownAsG almost 8 years
    @laalto, reading the C++ FAQ puts us back in the "have you read the man pages" days.
  • Wormer
    Wormer almost 5 years
    Though it's possible to make those wrapper functions call first time before main if we want it -- in a constructor of a static class
  • mada
    mada over 2 years
    "The most elegant way around it (..) is to wrap the initialization in a function." then you have fallen into static deinitialization order disaster if your object has not been constructed at the moment of destructing the calling object