Click here to Skip to main content
15,880,796 members
Articles / Programming Languages / C++

C++ Static Initialization Order: A New Solution to an Old Problem

Rate me:
Please Sign up or sign in to vote.
4.03/5 (8 votes)
26 May 2007CPOL8 min read 81.7K   178   28   18
Presents an easy method to control order of initialization for static objects

Introduction

There have been long threads about this in the past on CodeProject, although there don't seem to be any at the moment and it may be that this is considered by some a dead topic. For two good reasons, I don't think so. Firstly there are always new developers, especially on a forum like this. Secondly I have never been completely satisfied with any of the previous solutions I've seen. Overall I guess this article is an intermediate level article that might stump a few beginners (I tend to geek speak) but hopefully won't patronize all you C++ gurus out there. For those who've never come across this issue before, a quick review of the problem is in order.

The Problem

When you declare more than one static instance of any types in your C++ project, there is no guarantee about which order they will get constructed in. This is not a compiler flaw, it's part of the C++ specification.

For example:

C++
//A.h
class A
{
public:
    A();
    ~A();
...
};
C++
//B.h
class B
{
public: 
    B();
    ~B();
...
};
C++
//A.cpp
A s_A;
C++
//B.cpp
B s_B;

Which constructor gets called first, A() or B()?

The answer is, it entirely depends on what order the compiler happened to put the code together in and the next time you change something 'unrelated' and do another build, they might get called the other way around.

Why does it matter?

It starts to really matter when A() relies on s_B already having been constructed for example by calling:

C++
s_B.DebugPrint( _T("s_A is being constructed") ); 

Another related problem is that both A() and B() will get called while the underlying runtime library is still bootstrapping itself and if they try to do anything too 'clever', for example examine the command line passed to the program, bad things will happen that will likely crash your code before the debugger has even got itself hooked up to let you find out what's going on. A bad day and grumpy home coming is likely to follow and who knows what trouble that could cause.

This sort of static initialization order dependency does actually happen and there is real software in the field which is relying on a coincidence in build order for the fact that it even starts up. A very scary thought!

What Should A Solution Look Like?

Some people will say that the solution is not to use static instances but this is neither practical nor a real solution. Static instancing exists in the language for good reasons and there are a limited range of situations when it should in fact be used. What we would really like to be able to do is to take advantage of all the features of a static instance but add the guarantee that no object construction will take place during the runtime library bootstrap phase and that static objects will always get constructed on first use. In this way, we control the order of initialization by the order of usage and we make sure that the runtime is fully operational and main/Winmain has been called before the constructors of our static instances get called.

Clearly there is no zero cost simple solution that will get us these features and any solution should be lightweight, low overhead, easy to use, easy to understand, etc. in the same way that all such solutions should be. I've seen a number of proposed solutions over the years and all have suffered from one or more limitations of performance, typing overhead or applicability to different types.

My best attempt so far is called a Static Templated Object Block (sTOB). I thought I'd share it with you in case anyone else is still bugged by this rather old but tough problem.

Static Templated Object Block

Limitations

  1. It only works for class objects with default constructors.
    I don't consider this to be a very big problem. Non class objects are unlikely to be involved in causing initialization order issues as they have no constructors to call stuff that hasn't been initialized yet. Classes without default constructors aren't going to fly as statics anyway.
  2. There is a small overhead on each call to a sTOB but in a world of GHz processors, I can live with that for the benefits gained.
  3. There is a tiny allocation size overhead of half a dozen bytes for each sTOB. If you have massive arrays of static instances and this is an issue, then put them inside a single static 'wrapper' object and you'll only pay the overhead once.
  4. You can't declare a sTOB instance as a member of its own template parameter class because at the point the compiler tries to do this, it can't compute sizeof T to make the sTOB definition work. This is quite an annoying limitation and one it would be nice to get a solution to.
    To make this clear: The following will not compile :-(
    C++
    class CSomething
    {
        public:
        Declare_pseudo_static( CSomething ) s_Something;
    };

The Code

The concept is to statically allocate the memory for the object but not to construct it in that memory until it is first called. The object remains 'static' in that it has all the properties of a static instance except that a sTOB is treated as a pointer rather than a reference type in most cases. Think of it as a simple smart pointer that you don't have to initialize.

The code below is a verbatim header file with the comments taken out to non code text. You can reassemble it or just use the real thing out of the attached demo code.

C++
//sTOB.h
//Rev 1.0

#include< new >

Include the Standard library <new> header here to get placement new for use in the _Kick macros further down.

C++
template< class T >
struct sTOB
{

As this is demo code, I'll put the private interesting parts of the class first:

C++
private:

A real T pointer which points to start of the memory array. The union and dStuffing members are there purely to guarantee the alignment of the memory array which follows as suggested by Billy E.

C++
union
{
    T* m_pThis;
    double dStuffing;
};

A memory array large enough to contain a T:

C++
char m_InternalData[sizeof T];

Switch to record first call having been made:

C++
bool m_bInitialised;

The _Kick macro ensures that the real T is created in the statically allocated memory. This is the cause of the call overhead but it only really inserts a handful of assembler instructions in each case.

C++
#define _Kick \
    if( !m_bInitialised ) \
    { \
        /*Set initialized to true first to*/ \
        /*allow codependent constructions*/ \
        m_bInitialised = true; \
        /*T() is called indirectly here*/ \
        m_pThis = new(&m_InternalData[0]) T; \
    }    

Now the public interface part of the class:

C++
public:

We need to override all these operators. The client code may use any of them to access the sTOB the first time so each must _Kick.

The sTOB may be assigned from a real T:

C++
T& operator = (T _t)
    {
        _Kick
        *m_pThis = _t;
        return *m_pThis;
    }

To convert the sTOB into a simple T*:

C++
operator T*()
    {
        _Kick
        return m_pThis;
    }

A function may take a T& and a sTOB<T> be passed in:

C++
operator T&()
    {
        _Kick
        return  *m_pThis;
    }

The address of a smart T* should be a T** right.

C++
T** operator &()
    {
        _Kick
        return &m_pThis;
    }

This allows client code to call through the sTOB, e.g. mysTOB->GetCmdLine();

C++
T* operator ->()
    {
        _Kick
        return m_pThis;
    }

Construct the sTOB at static initialization time:

C++
sTOB() 
    {
        m_bInitialised = false;

This sets up the correct actual value for m_pThis but it won't be valid until after construction:

C++
    m_pThis = reinterpret_cast<T*>(m_InternalData);
}

Destruct the sTOB at static teardown time. Be aware that everything including some of the runtime may have disappeared by the time this is called.

C++
~sTOB()
{
    if(m_bInitialised)
    {

The T destructor is called here:

C++
            m_pThis->T::~T();
        }
    }
};

This gives us both the static allocation and first call construction guarantee. To make use of this transparently, we need a couple more trivial macros.

For class member declarations:

C++
#define Declare_pseudo_static( _I ) static sTOB< _I >

For file scope declarations:

C++
#define Implement_pseudo_static( _I ) sTOB< _I >

Now we can write:

C++
//A.h
class A
{
public:
    A();
    ~A();
...
};
C++
//A.cpp
Implement_pseudo_static( A ) s_A;
C++
//B.h
class B
{
public: 
    B();
    ~B();
...
};
C++
//B.cpp
Implement_pseudo_static( B ) s_B;

Now it no longer matters whether A() relies on s_B or B() relies on s_A or even both.

C++
A::A()
{
    s_B->Bark();//Safe as s_B gets constructed if it isn't already
}
C++
B::B()
{
    s_A->Squawk();//Safe as S_A gets constructed if it isn't already
}

Further Improvements

sTOB(s) gives you control over construction order but not destruction order with the current implementation. A Free function could be added that could be called directly on the sTOB, e.g. s_A.Free() which would call the internal object destructor and reset m_bInitailised. This would give the option of deterministic destruction order before the runtime teardown does it for you. I haven't needed this yet, probably because static objects are usually intended to have both maximum scope and extent by design.

Demo Code

The demo code is a VC8 solution which is intended to be run in Debug within the Visual Studio IDE. It demonstrates the use of sTOBs to control static initialization order as compared to uncontrolled standard static objects. It should back-port to MSVC6 without any trouble as the template usage is really simple but I haven't tried it. Please keep in mind that this is minimalist code for demo purposes only.

Conclusion

This is my first article for CodeProject and it certainly made me look very hard at the code I was about to publish and find a lot of flaws within it. I'm sure there are more to find but the process itself is encouraging: it makes my code better. If there are any takers, I may well do another article on a technique I call Flyer Instancing.

I'd welcome comments especially on improvements and extensions of the idea or any good uses it gets put to.

Acknowledgements

I'd like to thank Marc Clifton for his excellent piece on submitting articles to CodeProject without which I would never have completed this article and Jason Henderson for his Article Helper tool without which it would never have been publishable.

Revision History

  • 29th May, 2007: Updated to take account of potential alignment issues with the char array. See article comment from Billy E.
  • 26th May, 2007: Original article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO Querysoft Ltd
United Kingdom United Kingdom
I'm a UK based software engineering contractor, CEO of Querysoft Ltd, a candidate and activist for the UK Independence Party and occasionally I get time look at Code Project.

Comments and Discussions

 
Questiondefault constructors Pin
Nynaeve29-May-07 6:41
Nynaeve29-May-07 6:41 
AnswerRe: default constructors Pin
Matthew Faithfull29-May-07 7:43
Matthew Faithfull29-May-07 7:43 
GeneralRe: default constructors Pin
Nynaeve29-May-07 7:49
Nynaeve29-May-07 7:49 
GeneralRe: default constructors Pin
Matthew Faithfull29-May-07 8:53
Matthew Faithfull29-May-07 8:53 
GeneralRe: default constructors Pin
Nynaeve29-May-07 9:57
Nynaeve29-May-07 9:57 
GeneralRe: default constructors Pin
Matthew Faithfull29-May-07 11:25
Matthew Faithfull29-May-07 11:25 
GeneralMinor defects Pin
Billy E29-May-07 6:19
Billy E29-May-07 6:19 
GeneralRe: Minor defects Pin
Matthew Faithfull29-May-07 6:34
Matthew Faithfull29-May-07 6:34 
GeneralRe: Minor defects Pin
Billy E29-May-07 7:07
Billy E29-May-07 7:07 
GeneralRe: Minor defects Pin
Matthew Faithfull29-May-07 8:40
Matthew Faithfull29-May-07 8:40 
Thanks Bill,
I had a look at aligned_storage.hpp and it basically does exactly what you are suggesting it just wraps it up in a lot of templates and namespaces. I've tried it and it works although of course no difference is going to be visible. I'll update the article to take account of alignment.
"m_bInitialised" is a little more tricky, as you'll see I can't directly replace it with a test for m_pThis == 0 because m_pthis is never initialised to 0, rather it's initialised at static initialisation to what it will be after final construction, the address of m_InternalData[0]. The reasons for this may not be sound but essentially I wanted to simulate a 'real' static as closely as possible. For the moment I'm going to leave "m_bInitialised" in.

Nothing is exactly what it seems but everything with seems can be unpicked.

GeneralGood but... Pin
phgo28-May-07 23:03
phgo28-May-07 23:03 
GeneralRe: Good but... Pin
Matthew Faithfull28-May-07 23:33
Matthew Faithfull28-May-07 23:33 
GeneralRe: Good but... Pin
jho1965dk30-May-07 22:07
jho1965dk30-May-07 22:07 
GeneralRe: Good but... Pin
Matthew Faithfull30-May-07 22:58
Matthew Faithfull30-May-07 22:58 
GeneralRe: Good but... Pin
jho1965dk31-May-07 1:32
jho1965dk31-May-07 1:32 
GeneralNice Pin
cmk26-May-07 12:04
cmk26-May-07 12:04 
GeneralRe: Nice Pin
Matthew Faithfull26-May-07 12:36
Matthew Faithfull26-May-07 12:36 
GeneralRe: Nice Pin
cmk26-May-07 13:56
cmk26-May-07 13:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.