HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Header only c++ singleton pattern implementation

Submitted by: @import:stackexchange-codereview··
0
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.

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

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.