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.
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.
Which constructor gets called first,
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:
s_B.DebugPrint( _T("s_A is being constructed") );
Another related problem is that both
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
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
- 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.
- 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.
- 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.
- 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
Declare_pseudo_static( CSomething ) s_Something;
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.
#include< new >
Include the Standard library
<new> header here to get placement new for use in the
_Kick macros further down.
template< class T >
As this is demo code, I'll put the
private interesting parts of the class first:
T pointer which points to start of the memory array. The
dStuffing members are there purely to guarantee the alignment of the memory array which follows as suggested by Billy E.
A memory array large enough to contain a
char m_InternalData[sizeof T];
Switch to record first call having been made:
_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.
#define _Kick \
if( !m_bInitialised ) \
m_bInitialised = true; \
m_pThis = new(&m_InternalData) T; \
public interface part of the class:
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
sTOB may be assigned from a real
T& operator = (T _t)
*m_pThis = _t;
To convert the
sTOB into a simple
A function may take a
T& and a
sTOB<T> be passed in:
The address of a smart
T* should be a
T** operator &()
This allows client code to call through the
T* operator ->()
static initialization time:
m_bInitialised = false;
This sets up the correct actual value for
m_pThis but it won't be valid until after construction:
m_pThis = reinterpret_cast<T*>(m_InternalData);
static teardown time. Be aware that everything including some of the runtime may have disappeared by the time this is called.
T destructor is called here:
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:
#define Declare_pseudo_static( _I ) static sTOB< _I >
For file scope declarations:
#define Implement_pseudo_static( _I ) sTOB< _I >
Now we can write:
Implement_pseudo_static( A ) s_A;
Implement_pseudo_static( B ) s_B;
Now it no longer matters whether
A() relies on
B() relies on
s_A or even both.
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
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.
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.
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.
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.
- 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