Jonathan Boccara's blog

The Issues With Singletons and How to Fix Them

Published March 6, 2018 - 9 Comments

Singleton is one of the 23 design patterns of book of the Gang of Four, but over time it has evolved into an anti-pattern that developers tend to avoid nowadays.

Today we have a guest on Fluent C++, Mihai Sebea. Mihai is here today to share with us his experience about rooting out singletons, and in particular how signals provide a better alternative for keeping the code manageable.

singletons signals

Jonathan: Hello Mihai, and welcome to this interview for Fluent C++!

Mihai: Hello and thank you for inviting me 🙂

So Mihai, before getting into the meat of the subject, why don’t you tell us a little bit about yourself?

M: My name is Mihai Sebea. I have been a programmer for about 15 years now, most of them spent in C++ land. I have been working for Gameloft for the last 13 years. I spent 6 years in the Bucharest studio where I worked on projects like N.O.V.A 1 & 2, and 7 years in the sunny Barcelona studio on projects like Asphalt8 : Airborne and Despicable Me: Minion Rush  

You’ve had to deal with a lot of Singletons. Could you please briefly recap what a Singleton is, just to make sure everyone is in line?

M: Usually it’s class that can have only one instance throughout the lifetime of the application and a global way of accessing it. And when I say usually it’s because sometimes these rules are not respected by the implementation.

Ok, so what’s wrong exactly with singletons?

M: There are a number of issues that can pose problems and they are all researched and documented. First of all there is no standard implementation. There are multiple ways of implementing this design pattern.

For example you can have a singleton as a static variable and this is all fine if you have just ONE singleton object in your application.

class S 
{
public: 
    static S& GetInstance() 
    { 
        static S instance;
        return instance; 
    } 

private: 
    S() { /*...*/ };
    S(S const& other) = delete;
    S(S&& other) = delete;
};

But in my experience you never have just one system as a singleton. You have multiple occurrences of them and at some point they will depend on each other. So you will have no way of controlling the initialization order unless you do some obscure compiler tricks. And of course you will have no way of controlling the destruction order either.

So one approach you can try to use is to create the singletons “on demand”. That is, have a CreateInstance and FreeInstance methods that will allocate / deallocate the static variable and GetInstance will just return it . This will make it far easier to manage the order of construction and destruction but it will violate the rule that the singleton is available throughout the lifetime of the application.

What other issues have you met ?

M: Having to unit test your classes is notoriously difficult when they depend or interact with singletons. Or even worse… singleton classes are impossible to unit test since they should be available for the duration of the application. The best you can do is to make each unit test a separate application.

Also usually unit testing frameworks make heavy use of static variables for automated testing discovery and whatnot, so they might interfere with your own static variables.

The other BIG issue is multithreading. Since you are accessing a global variable you should protect all the members of this class so data is not read or written from multiple threads at the same time.

If singletons are so riddled with issues, why do we use them at all?

M: Well because it’s too damn easy and they do save a lot of time in the short term. Imagine you have a new feature to implement and in your design this new system needs to talk to existing systems. The existing systems are complicated and layered so you will need to pass your new system through all those layers all the way down to the object that needs to say something to your new system. It will take you a lot of time and you might introduce bugs… in particular if you don’t have unit tests!

Ok, so if it’s the easiest route, that’s the one you’re tempted to take given the constraints of a project, is that it?

M: You take the easy route of course … make your new system a singleton and directly on the object that needs to be notified of an event you just call a function from your new and shiny singleton and Done! You ship it!

It’s an easy route, but not a viable one, is it?

M: No, the biggest problem that this poses is scalability. This will not scale well on the long term. If you need to update and maintain your program for a long time the problem is only going to get worse.

Each new system you add will most likely be a singleton for the reasons stated above. Then you will add the singleton manager which will be responsible for creating and destroying your singletons. Then a new guy will come in the team and he won’t know or forget about the singleton manager and implement his system in another way. All of this will decay into an unmaintainable mess of global variables that you will need to throw away at some point and rewrite everything from scratch.

Ok Mihai, so what alternatives do you have to Singletons?

M: The “solution” imho is no to use any singleton classes. I know it sounds radical and downright impossible but I assure it is doable and the results are AMAZING. Start small and  take the time to DESIGN your systems. Design their lifetime, ownership and the way they interact with each other. Take for example the following set of classes:

class Logger
{
public:
    static Logger& GetInstance() { static Logger instance; return instance; }

    void Log(std::string const& message) 
    {
        std::cout << message << '\n';
    }
};

class Debugger
{
public: 	
    static Debugger& GetInstance() { static Debugger instance; return instance; }

    void Update()
    {
        Logger::GetInstance().Log("Debugger updating");
    }
};

class Profiler
{
public : 
    static Profiler& GetInstance() { static Profiler instance; return instance; }

    void Update()
    {
        Debugger::GetInstance().Update();	
        Logger::GetInstance().Log("Profiler updating");
    }
};

class Game
{
public:
    void Update()
    {
        Profiler::GetInstance().Update();
    }
};

As you can see each class accesses global variables from all over the place.

Now you can rewrite that so you no longer have singletons but member variables, so you control the lifetime, the order of creation and destruction and reason easier about what objects might be affected by a method call.

class Logger
{
public:
    void Log(std::string const& message) 
    {
        std::cout << message << '\n';
    }
};

class Debugger
{
public:     
    Debugger(Logger& logger)
    : m_logger(logger)
    {}

    void Update()
    {
        m_logger.Log("Debugger updating");
    }
private:
    Logger& m_logger;
};

class Profiler
{
public:
    Profiler(Logger& logger, Debugger& debugger) 
        : m_logger(logger)
        , m_debugger(debugger)
        {}
    void Update()
    {
        m_debugger.Update();    
        m_logger.Log("Profiler updating");
    }

private:
    Logger& m_logger;
    Debugger& m_debugger;
};

class Game
{
public:
    Game() 
        : m_logger()
        , m_debugger(m_logger)
        , m_profiler(m_logger, m_debugger)
    {

    }

    void Update()
    {
        m_profiler.Update();
    }
    
private:
    Logger m_logger;
    Debugger m_debugger;
    Profiler m_profiler;
};

But this still has the problem the the objects are too tightly coupled. Now what we can do here, instead of thinking that system A needs to call a method from system B, is trying to think that system A will send out a signal and system B listens to this signal.

What this will do is make the systems decoupled. Some other system that sits above and knows  the two will connect them and take care of lifetimes.  This alone has huge benefits in terms of code readability, compilation time, writing tests and so on.

In our previous example this could look like this:

class Logger
{
public:
    void Log(std::string const& message) 
    {
        std::cout << message << '\n';
    }
};

class Debugger
{
public:     
    void Update()
    {
        sigLog.emit("Debugger updating")
    }

    Signal<void(std::string)> sig_Log;
};

class Profiler
{
public:
    Profiler::Profiler()
    void Update()
    {
        sig_Update.emit()
        sig_Log.emit("Profiler updating")
    }

    Signal<void> sig_Update;
    Signal<void(std::string)> sig_Log;
};

class Game
{
public:
    Game() 
        : m_logger()
        , m_debugger()
        , m_profiler()
        , m_debuggerLoggerConnection(m_debugger.sig_Log.connect(&Logger::Log, m_logger))
        , m_profilerLoggerConnection(m_profiler.sig_Log.connect(&Logger::Log, m_logger))
        , m_profilerDebuggerConnection(m_profiler.sig_Update.connect(&Debugger::Update, m_debugger))
    {}

    void Update()
    {
        m_profiler.Update();
    }
    
private:
    Logger m_logger;
    Debugger m_debugger;
    Profiler m_profiler;

    Connection m_debuggerLoggerConnection;
    Connection m_profilerLoggerConnection;
    Connection m_profilerDebuggerConnection;
};

Could you please tell us more about signals?

M: We were inspired by Boost and Qt signal systems. We use Qt extensively in our tools and we tried to use Boost in our game code but it proved too heavy especially for the compile times :).

As for the implementation, a signal is simply a list of functions to be called when you call an emit method. A nice thing to have here is when you connect a signal to a certain object and method to receive a connection object. If the connection object is destroyed then the signal is automatically disconnected from your object.

In Qt code that looks like this:

auto m_connection = QObject::connect(lineEdit, &QLineEdit::textChanged, this, setText);

If m_connection is a member of this class then when it goes out of scope it will also disconnect the object from the signal. This is a easy way to ensure that you don’t end up with dangling pointers.

Where can our readers find an implementation of signals? Is it easy enough to be coded on the fly? Or are there libraries available? Boost?

M: I think a simple implementation can whipped up in no time but depending on the complexity of the project, the requirements, the time constraints you might be better off using existing implementations if they fit.

One small point I do have to make here is try to avoid header only libraries especially if you are going to drag them into your headers and throughout your whole project. While you might save a few minutes or hours by not setting up a project for the library to link with it … you will lose a lot more in compilation time (depending on the number of people involved and the scope of the project)  .

Mihai, is there anything you’d like to add to this interview and that I forgot to mention?

M:  Not on the technical side. While I’m here though, I would like to take the opportunity to mention that we are hiring at Gameloft all over the world 🙂

Before I let you go, could you please tell us where people can find you online?

M: You can find me on twitter @mihai_sebea.

Thanks a lot Mihai, it was great to have you on!

M: Thank you for inviting me and thanks to everyone for taking the time to read everything!

 

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin

Comments are closed