patterncppMinor
Header only c++ singleton pattern implementation
Viewed 0 times
headersingletonimplementationonlypattern
Problem
I want to implement the singleton pattern in a header only c++ library. (Please refrain from sharing your opinion on the pattern itself!) This would be trivial if I could rely on the compiler implementing thread safe c++11 function-local static-duration object initialization. i.e.
Unfortunately MSVC prior to 2015 does not comply with this standard. See "Magic Statics" so I have tried to implement it through a class:
So my function now looks like:
I hope that this function is thread safe given that the ctor of
static inline MyClass& singleTon()
{
static MyClass instance;
return instance;
}Unfortunately MSVC prior to 2015 does not comply with this standard. See "Magic Statics" so I have tried to implement it through a class:
#include
/**
* Header only implementation of the singleton pattern.
* Use like this:
* MyClass& singleton = SingleTon().get();
*/
template
struct SingleTon
{
static std::unique_ptr instance;
static std::once_flag flag;
Type& get()
{
auto& capture = instance;// lambdas can't capture static members
std::call_once(flag, [&capture] { capture.reset(new Type{}); });
return *instance.get();
}
};
template
std::unique_ptr SingleTon::instance;
template
std::once_flag SingleTon::flag;So my function now looks like:
static inline MyClass& singleton()
{
return SingleTon().get();
}I hope that this function is thread safe given that the ctor of
Typeis nothrow.Solution
I have a problem with this because
This is still a static storage duration object. This means its initialization can happen after a call to
So the instance variable needs to be wrapped in a function to make sure initialization of
How about this:
The Elephant in the room (Linker)
Now that we solved this issue. There is a larger elephant in the room to solve. (which is also true for the original code).
Since this is a template class with its whole definition in the header file there will be several versions of this function spread around the code (one in each compilation unit). It becomes the job of the linker to remove all but one copy. If you have a simple application that just uses object files this does not seem to be an issue.
BUT I have come across linkers that were unable to do this when the object were compiled into separate dynamically loaded libraries. Thus in effect your singleton becomes a singleton per runtime library that uses it. This is why you don't find Singelton libraries defined like this with a template class as the harness.
template
struct SingleTon
{
static std::unique_ptr instance;This is still a static storage duration object. This means its initialization can happen after a call to
Type& get(). So you have to solve this problem first.So the instance variable needs to be wrapped in a function to make sure initialization of
instance is guaranteed. If you are going to do that you may as well not dynamically create the object.How about this:
template
struct SingleTon
{
private:
static Type& instance()
{
// Use static function scope variable to
// correctly define lifespan of object.
static Type instance;
return instance;
}
public:
static Type& get()
{
// Note the constructor of std::once_flag
// is a constexpr and is thus done at compile time
// thus it is immune to multithread construction
// issues as nothing is done at runtime.
static std::once_flag flag;
// Make sure all threads apart from one wait
// until the first call to instance has completed.
// This guarantees that the object is fully constructed
// by a single thread.
std::call_once(flag, [] { instance(); });
// Now all threads can go get the instance.
// as it has been constructed.
return instance();
}
};The Elephant in the room (Linker)
Now that we solved this issue. There is a larger elephant in the room to solve. (which is also true for the original code).
Since this is a template class with its whole definition in the header file there will be several versions of this function spread around the code (one in each compilation unit). It becomes the job of the linker to remove all but one copy. If you have a simple application that just uses object files this does not seem to be an issue.
BUT I have come across linkers that were unable to do this when the object were compiled into separate dynamically loaded libraries. Thus in effect your singleton becomes a singleton per runtime library that uses it. This is why you don't find Singelton libraries defined like this with a template class as the harness.
Code Snippets
template<class Type>
struct SingleTon
{
static std::unique_ptr<Type> instance;template<class Type>
struct SingleTon
{
private:
static Type& instance()
{
// Use static function scope variable to
// correctly define lifespan of object.
static Type instance;
return instance;
}
public:
static Type& get()
{
// Note the constructor of std::once_flag
// is a constexpr and is thus done at compile time
// thus it is immune to multithread construction
// issues as nothing is done at runtime.
static std::once_flag flag;
// Make sure all threads apart from one wait
// until the first call to instance has completed.
// This guarantees that the object is fully constructed
// by a single thread.
std::call_once(flag, [] { instance(); });
// Now all threads can go get the instance.
// as it has been constructed.
return instance();
}
};Context
StackExchange Code Review Q#147407, answer score: 5
Revisions (0)
No revisions yet.