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:
class A
{
public:
A();
~A();
...
};
class B
{
public:
B();
~B();
...
};
A s_A;
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:
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
- 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 :-(
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.
#include< new >
Include the Standard library <new>
header here to get placement new for use in the _Kick
macros further down.
template< class T >
struct sTOB
{
As this is demo code, I'll put the private
interesting parts of the class first:
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.
union
{
T* m_pThis;
double dStuffing;
};
A memory array large enough to contain a T
:
char m_InternalData[sizeof T];
Switch to record first call having been made:
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.
#define _Kick \
if( !m_bInitialised ) \
{ \
\
\
m_bInitialised = true; \
\
m_pThis = new(&m_InternalData[0]) T; \
}
Now the public
interface part of the class:
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
:
T& operator = (T _t)
{
_Kick
*m_pThis = _t;
return *m_pThis;
}
To convert the sTOB
into a simple T*
:
operator T*()
{
_Kick
return m_pThis;
}
A function may take a T&
and a sTOB<T>
be passed in:
operator T&()
{
_Kick
return *m_pThis;
}
The address of a smart T*
should be a T**
right.
T** operator &()
{
_Kick
return &m_pThis;
}
This allows client code to call through the sTOB
, e.g. mysTOB->GetCmdLine();
T* operator ->()
{
_Kick
return m_pThis;
}
Construct the sTOB
at static
initialization time:
sTOB()
{
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);
}
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.
~sTOB()
{
if(m_bInitialised)
{
The T
destructor is called here:
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:
#define Declare_pseudo_static( _I ) static sTOB< _I >
For file scope declarations:
#define Implement_pseudo_static( _I ) sTOB< _I >
Now we can write:
class A
{
public:
A();
~A();
...
};
Implement_pseudo_static( A ) s_A;
class B
{
public:
B();
~B();
...
};
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.
A::A()
{
s_B->Bark();}
B::B()
{
s_A->Squawk();}
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 sTOB
s 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