Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++/CLI

Create Singleton with Parameters in Constructor

3.75/5 (11 votes)
6 Apr 2010Zlib2 min read 1   328  
This article describes how you can create a singleton which has a constructor with one or more parameters.

Introduction

This article will show how you can create a singleton which uses parameters in its constructor. It's rare when it needs to add parameters to the singleton on construct, but sometimes it is necessary. Imagine that you have a class named Platform which contains factories and it creates platform dependent objects (mutexes, threads and file system objects). We don't want to write this Platform class in every operating system and we want to use only one Platform singleton to create platform dependent objects. We are using interfaces (IThread, IMutex) and implementing it on all platforms. On the initialization of the Platform, we give these platform dependent objects and the Platform will clone them when we need a new mutex or thread.

This article won't introduce you to a full featured singleton. My goal is to make a simple singleton which requires parameters in its constructor. If you want to learn more about singletons, I suggest you read Andrei Alexandrescu's book Modern C++ Design: Generic Programming and Design Patterns Applied.

Conception and the Goal

I need a clear, simple and easy to use solution, like this:

C++
// create and initialize the singleton
SingletonFactory<Platform>().create(new Thread_linux, new Mutex_linux);
// get and use the singleton
IThread* pThread = Platform::instance()->threadFactory()->create();

The expectations are the follows:

  • The instantiation of the singleton and the query needs to be separate. Don't use one function for two different operations.
  • Only on the creation and only once, you need to pass the parameters.
  • The number of parameters have to be variable.
  • Clear and simple solution.
  • Have an option to instantiate the singleton at compile time and also check it at compile time.

The Singleton

First, let's make a template singleton class, which will create any type of singleton for us.

C++
template <typename _Ty>
class Singleton
{
    friend class SingletonFactory<_Ty>;

protected:

    Singleton()
    {
    }

    Singleton(const Singleton& other)
    {
    }

    Singleton & operator=(const Singleton& other)
    {
	return *this;
    }

public:

    static _Ty* instance()
    {
	return mp_Instance.get();
    }

protected:

    static std::auto_ptr<_Ty> mp_Instance;
};

#ifndef SINGLETON_COMPILE_TIME_CHECK
template <typename _Ty> std::auto_ptr<_Ty> Singleton<_Ty>::mp_Instance;
#endif

I use auto_ptr to release the instance to keep the code simple. I enclose the initialization of the static mp_Instance with an ifdef macro, because if you define the SINGLETON_COMPILE_CHECK, you have to initialize the static object. It constrains you to initialize it and do it only once.

Now we can write a factory which will create the singletons.

The Singleton Factory

C++
template <typename _TSingleton>
class SingletonFactory
{
public:

    virtual ~SingletonFactory() {}

    _TSingleton* create()
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton();
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TParam>
    _TSingleton* create(_TParam param)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(param);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TP1, typename _TP2>
    _TSingleton* create(_TP1 p1, _TP2 p2)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(p1, p2);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TP1, typename _TP2, typename _TP3>
    _TSingleton* create(_TP1 p1, _TP2 p2, _TP3 p3)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(p1, p2, p3);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TP1, typename _TP2, typename _TP3, typename _TP4>
    _TSingleton* create(_TP1 p1, _TP2 p2, _TP3 p3, _TP4 p4)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(p1, p2, p3, p4);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TP1, typename _TP2, typename _TP3, 
		typename _TP4, typename _TP5>
    _TSingleton* create(_TP1 p1, _TP2 p2, _TP3 p3, _TP4 p4, _TP5 p5)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(p1, p2, p3, p4, p5);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TP1, typename _TP2, typename _TP3, 
		typename _TP4, typename _TP5, typename _TP6>
    _TSingleton* create(_TP1 p1, _TP2 p2, _TP3 p3, _TP4 p4, _TP5 p5, _TP6 p6)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(p1, p2, p3, p4, p5, p6);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TP1, typename _TP2, typename _TP3, 
		typename _TP4, typename _TP5, typename _TP6, typename _TP7>
    _TSingleton* create(_TP1 p1, _TP2 p2, _TP3 p3, _TP4 p4, _TP5 p5, _TP6 p6, _TP7 p7)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(p1, p2, p3, p4, p5, p6, p7);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    template <typename _TP1, typename _TP2, typename _TP3, 
	typename _TP4, typename _TP5, typename _TP6, typename _TP7, typename _TP8>
    _TSingleton* create(_TP1 p1, _TP2 p2, _TP3 p3, _TP4 p4, 
		_TP5 p5, _TP6 p6, _TP7 p7, _TP8 p8)
    {
	if (isCreated)
	    return _TSingleton::mp_Instance.get();

	isCreated = true;
	_TSingleton* pTmp = new _TSingleton(p1, p2, p3, p4, p5, p6, p7, p8);
	_TSingleton::mp_Instance.reset(pTmp);

	return _TSingleton::mp_Instance.get();
    }

    void destroy()
    {
	_TSingleton::mp_Instance.reset();
	isCreated = false;
    }

private:
    
    static bool isCreated;
};

template <typename _TSingleton> bool SingletonFactory<_TSingleton>::isCreated = false;

It can handle up to 8 constructor parameters. You can write more if you need.

Final Step

The MySingleton class is more simple than ever:

C++
class NonCopyable
{
protected:

    NonCopyable(const NonCopyable& other) {}
    NonCopyable& operator=(const NonCopyable& other) { return *this; }
};

class MySingleton : private NonCopyable, public Singleton<MySingleton>
{
    friend class SingletonFactory<MySingleton>; // to access the private constructor
private:

    MySingleton(int foo) : m_Foo(foo) {}

private:

    int m_Foo;
};

#ifdef SINGLETON_COMPILE_TIME_CHECK
// initializing the singleton, to avoid linker error
template <> std::auto_ptr<MySingleton> Singleton<MySingleton>::mp_Instance = 
    std::auto_ptr<MySingleton>(SingletonFactory<MySingleton>().create(15));
#endif

That's it, the private inheritance is guarantee that nobody can inherit this class.

If you want to initialize at compile time, just define the SINGLETON_COMPILE_TIME_CHECK macro and initialize the mp_Instance as you see above. If you don't know the initialization values at compile time, you have to call the SingletonFactory<_TSingleton>::create() function in your code.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License