Introduction
There is a bug in the Visual C++ 2003 compiler that prevents the declaration of a "Meyers singleton" in a header under certain circumstances. This article describes how to reproduce it and how to work around it.
Background
Meyers Singleton
Skip this section if you are already familiar with the concept.
A "Meyers singleton" is a static
class method that returns the address of a static
local variable. For example:
class cFoo
{
public:
static cFoo &getIt()
{
static cFoo it;
return it;
}
int mVariable;
};
The local static
is constructed the first time the method is called and destroyed when the runtime library shuts down (atexit
). I won't do a better job explaining this than the man who it is named after (Scott Meyers). I believe it is described in his book More Effective C++.
The Bug
In Visual C++ 2003, including a "Meyers singleton" in a header file will not work correctly if:
- You include the header in more than one object file and...
- You call the
getIt
method from more than one object file and... - You have optimisations enabled
Which is the position I found myself in. Incidentally, I have no idea if this is totally reproducible, but I hope somebody out there hits the same wall I did and finds this article. What seems to happen is that the linker does correctly identify all instances of it
as being one and the same. However, you may not know that a static
variable constructor is protected from being called more than once by a compiler-generated flag variable. It seems that the linker does not correctly treat all of these as one and the same. The result is that a single instance gets constructed more than once, and more importantly its destructor is put on the runtime library atexit
list more than once. When the runtime library shuts down, the object gets destroyed twice and not surprisingly the second one blows up.
Using the Code
What I discovered was that the Visual C++ 2003 linker deals quite happily with a local static
variable that is contained in a non-static inline method:
template <
typename T
>
class cFoo
{
public:
struct sEmpty
{
cFoo &getIt()
{
static cFoo it;
return it;
}
};
static cFoo &getIt()
{
sEmpty empty;
return empty.getIt();
}
T mVariable;
};
So the above code works even when multiply is included and called from more than one object file. There are a number of ways one could now solve this problem. I chose to create a template class to minimise repeated typing effort. Note the caveats in the comments well!
template <
typename tMakeUnique,
int tMakeUnique2 = 0
>
struct tVisualStudio2003InlineStatic
{
template <
typename tAny
>
tAny &get()
{
static tAny mIt;
return mIt;
}
template <
typename tAny,
typename tParam0Type
>
tAny &get (
tParam0Type param
)
{
static tAny mIt ( param );
return mIt;
}
};
History
- Published article
- Removed incorrect and irrelevant material about linker errors. Added reference to source