Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Visual C++ 2003 Meyers Singleton Bug

4.67/5 (3 votes)
7 Jun 2007CPOL2 min read 1  
A description of a bug in the Visual C++ 2003 compiler and a workaround

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:

C++
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:

C++
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!

C++
//---------------------------------------------------------
/// This hack works around a compiler bug in Visual C++ 
/// 2003 that causes incorrect code to be generated with
/// static inline class methods that contain static
/// variable declarations when optimizations are switched
/// on. It works by declaring a non-static method that 
/// contains a static variable and returns a reference to
/// it. Where you might normally write:
///
/// \code
/// cMyClass &getIt()
/// {
///     static cMyClass it;
///     return it;
/// }
/// \endcode
///
/// You now write:
///
/// \code
/// cMyClass &getIt()
/// {
///     tVisualStudio2003InlineStatic < cMyClass > it;
///     return it.get < cClassIdentifier >( getIt );
/// }
/// \endcode
///
/// The template parameter tMakeUnique is not used, except
/// that it creates a unique instance of this template. 
/// Usually you will provide the class that the static
/// method is a member of. If you need more than one
/// unique, static instance per class then specify
/// different integer values in the tMakeUnique2 parameter
/// for each one.
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

License

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