How Is Meyers' Implementation of a Singleton Actually a Singleton

How is Meyers' implementation of a Singleton actually a Singleton

This is a singleton because static storage duration for a function local means that only one instance of that local exists in the program.

Under the hood, this can very roughly be considered to be equivalent to the following C++98 (and might even be implemented vaguely like this by a compiler):

static bool __guard = false;
static char __storage[sizeof(Singleton)]; // also align it

Singleton& Instance() {
if (!__guard ) {
__guard = true;
new (__storage) Singleton();
}
return *reinterpret_cast<Singleton*>(__storage);
}

// called automatically when the process exits
void __destruct() {
if (__guard)
reinterpret_cast<Singleton*>(__storage)->~Singleton();
}

The thread safety bits make it get a bit more complicated, but it's essentially the same thing.

Looking at an actual implementation for C++11, there is a guard variable for each static (like the boolean above), which is also used for barriers and threads. Look at Clang's AMD64 output for:

Singleton& instance() {
static Singleton instance;
return instance;
}

The AMD64 assembly for instance from Ubuntu's Clang 3.0 on AMD64 at -O1 (courtesy of http://gcc.godbolt.org/ is:

instance():                           # @instance()
pushq %rbp
movq %rsp, %rbp
movb guard variable for instance()::instance(%rip), %al
testb %al, %al
jne .LBB0_3
movl guard variable for instance()::instance, %edi
callq __cxa_guard_acquire
testl %eax, %eax
je .LBB0_3
movl instance()::instance, %edi
callq Singleton::Singleton()
movl guard variable for instance()::instance, %edi
callq __cxa_guard_release
.LBB0_3:
movl instance()::instance, %eax
popq %rbp
ret

You can see that it references a global guard to see if initialization is required, uses __cxa_guard_acquire, tests the initialization again, and so on. Exactly in almost every way like version you posted from Wikipedia, except using AMD64 assembly and the symbols/layout specified in the Itanium ABI.

Note that if you run that test you should give Singleton a non-trivial constructor so it's not a POD, otherwise the optimizer will realize that there's no point to doing all that guard/locking work.

Is Meyers' implementation of the Singleton pattern thread safe?

In C++11, it is thread safe. According to the standard, §6.7 [stmt.dcl] p4:

If control enters
the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

GCC and VS support for the feature (Dynamic Initialization and Destruction with Concurrency, also known as Magic Statics on MSDN) is as follows:

  • Visual Studio: supported since Visual Studio 2015
  • GCC: supported since GCC 4.3

Thanks to @Mankarse and @olen_gam for their comments.


In C++03, this code wasn't thread safe. There is an article by Meyers called "C++ and the Perils of Double-Checked Locking" which discusses thread safe implementations of the pattern, and the conclusion is, more or less, that (in C++03) full locking around the instantiating method is basically the simplest way to ensure proper concurrency on all platforms, while most forms of double-checked locking pattern variants may suffer from race conditions on certain architectures, unless instructions are interleaved with strategically places memory barriers.

How to use Meyer's singleton without copy constructor?

The point is, you make the copy constructor, the move constructor, the copy assignment operator, the move assignment operator deleted and then you make the usable constructors private, so no one else can copy it:

class Singleton
{
public:
Singleton(Singleton const&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton const&) = delete;
Singleton& operator=(Singleton&&) = delete;

static auto& instance()
{
static Singleton obj;
return obj;
}

private:
Singleton() { /* ... */ }
};

int
main()
{
auto& ref = Singleton::instance(); // OK
auto copy = Singleton::instance(); // Won't compile
}

C++ Meyer's Singleton with arguments

You can just store the initialization parameters in statics. Example:

class S {
public:
static void Init(int i)
{
i_ = i;
initialized_ = true;
}

static S& getInstance()
{
if (!initialized_) {
throw SomeException;
}
static S instance(i_);
return instance;
}

private:
S(int) { }

static int i_;
static bool initialized_;
};

Remember to actually define the statics in the implementation (.cpp) file:

int S::i_ = 0;
bool S::initialized_ = false;

Obviously you could use Meyer singletons as well for these, but since they're built-in types and do not depend on other data, you wouldn't really gain much.

Singleton lifetime beyond returning reference

You need to review storage classes in C++.
Any object declared as static (or extern) has static storage and gets destructed at the end of program in the reverse order of construction.
The order of construction of naked static objects is not deterministic and troublesome, especially in multi threading.
Function local static Objects(AKA Scott Meyer's singletons) OTH are ALAP constructed on first call to the owning function, and since C++11 in a magically thread safe manor(ie. no double construction).
I would add following declarations to your friend's class to make the singleton actually single:

class Singleton{
//...
Singleton(Singleton const&)=delete;
Singleton(Singleton&&)=delete;
auto& operator=(Singleton const&)=delete;
auto& operator=(Singleton &&)=delete;
};

C++ Singleton design pattern

In 2008 I provided a C++98 implementation of the Singleton design pattern that is lazy-evaluated, guaranteed-destruction, not-technically-thread-safe:

Can any one provide me a sample of Singleton in c++?

Here is an updated C++11 implementation of the Singleton design pattern that is lazy-evaluated, correctly-destroyed, and thread-safe.

class S
{
public:
static S& getInstance()
{
static S instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
private:
S() {} // Constructor? (the {} brackets) are needed here.

// C++ 03
// ========
// Don't forget to declare these two. You want to make sure they
// are inaccessible(especially from outside), otherwise, you may accidentally get copies of
// your singleton appearing.
S(S const&); // Don't Implement
void operator=(S const&); // Don't implement

// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
public:
S(S const&) = delete;
void operator=(S const&) = delete;

// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
};

See this article about when to use a singleton: (not often)

Singleton: How should it be used

See this two article about initialization order and how to cope:

Static variables initialisation order

Finding C++ static initialization order problems

See this article describing lifetimes:

What is the lifetime of a static variable in a C++ function?

See this article that discusses some threading implications to singletons:

Singleton instance declared as static variable of GetInstance method, is it thread-safe?

See this article that explains why double checked locking will not work on C++:

What are all the common undefined behaviours that a C++ programmer should know about?

Dr Dobbs: C++ and The Perils of Double-Checked Locking: Part I

Leaky Meyers Singleton: is it threadsafe?

Yes, this is thread-safe: no two threads will ever try to initialize the same variable with static storage duration at the same time. That includes the entirety of evaluating the initializer.

Can we force all implementations to be singleton (objects)?

To our advantage all objects extend an interface called Singleton.
You can't extend it directly but we can use a Scala feature called self-types to enforce that all subtypes of TypeParser to also be Singletons (i.e. objects)

abstract class TypeParser[C <: Changes : TypeTag] extends Serializable {
self: Singleton =>
...
}


Related Topics



Leave a reply



Submit