Click here to Skip to main content
15,921,156 members
Articles / Programming Languages / C++/CLI

Create Singleton with Parameters in Constructor

Rate me:
Please Sign up or sign in to vote.
3.75/5 (11 votes)
6 Apr 2010Zlib2 min read 59.4K   328   29   4
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


Written By
Employed (other) http://www.otpbank.hu
Hungary Hungary

Comments and Discussions

 
GeneralMy vote of 2 Pin
Emilio Garavaglia6-Apr-10 21:27
Emilio Garavaglia6-Apr-10 21:27 
GeneralMy vote of 1 Pin
muhahahahahahaa6-Apr-10 5:05
muhahahahahahaa6-Apr-10 5:05 
QuestionSingleton? Pin
supercat98-Mar-10 11:06
supercat98-Mar-10 11:06 
An easy alternative to using a singleton is to just have a global reference to an object which gets set once on program initialization. The disadvantage of that approach is that one has to actually have code to ensure that the object gets initialized before it is used.

The normal pattern for a singleton is to have it instantiate a global object when it is referenced if that object doesn't already exist; one doesn't need to worry about which reference results in the object's creation, since the object will act the same regardless. This will work without requiring any pre-initialization other than setting the object reference to null.

If the creation of the singleton will require some information, whatever action is necessary to supply that information will have to be done before the first attempt to use the singleton. Once that is required, what advantage is there to using a singleton pattern rather than simply creating a conventional global object that's configured as desired, or (2) having a singleton that can create itself without parameters but supports a SetProperties method to configure it as desired? The former approach would seem far more versatile, and would generally be my choice.
AnswerRe: Singleton? Pin
Gergely Mancz11-Mar-10 0:20
professionalGergely Mancz11-Mar-10 0:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.