Click here to Skip to main content
Click here to Skip to main content

Visual C++ 2003 Meyers Singleton Bug

By , 7 Jun 2007
 

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!

//---------------------------------------------------------
/// 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)

About the Author

Stephen Boissiere
Software Developer (Senior) Olduwan Ltd.
United Kingdom United Kingdom
Member
Entertainment software.
Web development.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralAlso happens in VC6memberbhoman16 Jul '07 - 13:35 
I found the same problem in VC6; I based my implementation on the Meyer's books.
Since I'm not using a template in this case, I was able to move the method that declares the static singleton into the .cpp file and got around the problem.
I was quite glad to find this post as I thought I was using the "Meyers" singleton method incorrectly.
GeneralInline functions will do that to youmemberParchandri15 Jun '07 - 2:30 
I'm not sure it is a compiler bug, I think its a general C language issue. When you declare the class's member function in the header file, that means that function will get inlined, like when you declare a non-member function inline.
 
The problem with inline functions and static storage is that
-the function gets re-declared in every compilation unit (.c file)
-inline functions and static storage-class variables have no external linkage, so different compilation units know nothing about each other's inline functions or static storage-class variables.
-each static variable declaration is therefore totally local to its own compilation unit.
 
What you should do is move the implementation of the function out of the header file into the c file, to give the function external linkage, stop the function from being inlined, and make sure only one copy of the function is ever called.
GeneralRe: Inline functions will do that to youmemberStephen Boissiere15 Jun '07 - 3:21 
Hi. I'm afraid I don't have access to the C++ standard. Do you? If so could you see if there is a part specifically about static variables in inline methods and post it please?
 
It has always been my understanding that local static variables will be linked together as one single instance despite the inline function being potentially duplicated in more than one translation unit, and indeed this is how it works in my workaround or if I disable optimisations.
 
The trouble with moving the implementation of the function to a CPP file is that it is usually not possible for template classes. I left the templates out of my article to keep it simple, but in reality my "static inline member function with static local variable" is a member of a template class, and I cannot know in advance which types it will be instantiated for.
GeneralRe: Inline functions will do that to youmemberParchandri15 Jun '07 - 14:25 
I wonder if your inline function isn't really being inlined when you don't enable optimizations?
GeneralRe: Inline functions will do that to youmemberParchandri15 Jun '07 - 14:32 
This is from the current C standard rather than C++... but C++ has backwards compatibility considerations:
 
"An inline definition of a function with external linkage shall not contain a definition of a
modifiable object with static storage duration, and shall not contain a reference to an
identifier with internal linkage."
GeneralRe: Inline functions will do that to youmemberStephen Boissiere18 Jun '07 - 4:09 
FWIW, GCC seems to behave the way I expect (links the static as a single global instance) as well (also with optimisations enabled).
 
I have posted a message on the forums to see if anybody can come up with a definitive answer.
GeneralRe: Inline functions will do that to youmemberParchandri19 Jun '07 - 0:22 
OK, will be interested to see what it is. I have been assuming it works similarly in C++ to C, but there may be special rules for object member functions, that override the 'simplest' translation of C++ code to C code...
GeneralAbout the template static membersmemberWanderley Caloni7 Jun '07 - 9:18 
Hi! Thanks for the article.
 
I would like to point out that the static template member is no a problem as it seems. You can define them using the template format:
 

template <
typename T
>
class cFoo
{
public:
static cFoo &getIt()
{
return mIt;
}
T mVariable;
private:
static cFoo mIt;
};
 
///
/// This defines static member for all used types
///
template
cFoo cFoo::mIt;
 
int main()
{
cFoo& cfInt = cFoo::getIt();
cFoo& cfChar = cFoo::getIt();
}

 
--------------------
Wanderley Caloni Jr.
 
http://www.cthings.org

GeneralRe: About the template static membersmemberStephen Boissiere7 Jun '07 - 23:11 
Thanks, that is really useful! I wish I'd known that ages ago. After experimenting I found that the syntax is actually:
 
template < typename T > cFoo < T >::mIt;
 
I also just realised that is probably what you put in your message too but the < and > got swallowed as markup. I've done some reading and in fact there is a different reason for using the Meyer's singleton to do with when the static is first constructed. I will revise the article.
GeneralRe: About the template static members [modified]memberAndriy Tylychko6 May '09 - 21:39 
so what is the simplest solution? was the article revised as promised?
 
what can you say about this code (member function is not templte)? will it workaround the problem?
 
template <typename T>
struct Inline_static_workaround
{
	T& get()
	{
		static T t;
		return t;
	}
};

 
Today is tomorrow you worried about yesterday and all is well.
modified on Thursday, May 7, 2009 3:53 AM

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 7 Jun 2007
Article Copyright 2007 by Stephen Boissiere
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid