Code: Select all
MyClass::getSingleton().doStuff();Code: Select all
if (MyClass::getSingletonPtr() != NULL) MyClass::getSingleton().doStuff();I've recently made a simple effective, safe, and powerful class helper for adding singletons that allows you to access them very easily:
Code: Select all
MyClass->doStuff();
if (MyClass != NULL) MyClass->doStuff();
if (MyClass) MyClass->doStuff();Code: Select all
MyClass = new Singleton::MyClass();Code: Select all
MyClass = NULL;Why don't more people use a method like this? The method used by Ogre for example is tedious, error prone, and takes a lot more typing effort overall. As far as I can tell the only flaw with this method is that it makes singletons too easy, since it's not good to use singletons too much, but this is no excuse for a bad singleton implementation
I guess maybe you could argue that the "getSingleton()" notation makes it explicitly clear that you're using a singleton, but so does my method: if you use the naming convention where class names are always capitalized, and functions/variables always begin in lower case, there will never be any confusion with the examples shown above since C++ does not support "MyClass->functionCall();" and it could not possibly be interpreted as anything but a singleton. Even if you use another naming convention, you can still use prefixes to distinguish singletons.
Edit: Here is the full file & documentation (SmartSingleton.h):
Code: Select all
/* Copyright (c) 2009 John Judnich
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef _SMARTSINGLETON_H__
#define _SMARTSINGLETON_H__
//----------------------------------------------------------------------------------------------
// Documentation
/** \brief A powerful, no overhead (inline), easy to use, safe, hassle free singleton implementation helper.
Primarily, this class was intended to eliminate the use of the tedious "MyClass::getSingleton()."
notation often used, but also offers other time saving and safety features. Internally it's
not much more than a vague form of safe/smart pointer designed specially for singletons.
Using SmartSingleton's also allows you to define class singletons easily without modifying your
class definitons (allowing easy "singleton-ization" or "de-singleton-ization"). It also enables
the use of non-singleton instances of the same class if you want to do this.
Instructions on how to use a SmartSingleton will be described below, but first some examples
showing how it's used:
Initializing a singleton instance is extremely easy. Example:
"MyClass = new Singleton::MyClass();"
Using the singleton is even easier:
"MyClass->functionCall();"
"MyClass->doStuff();"
The singleton instance will be automatically deleted when your program shuts down. However,
if you need to delete the instance at a certain time, this is extremely easy:
"MyClass = NULL;"
You can also check if you've initialized an instance. Examples:
"if (MyClass != NULL) { ... }"
"if (MyClass) { ... }"
Implementing your classes using SmartSingleton required very minor changes. Here is one example:
//-- MyFile.h --
...
namespace Singleton //<-- move your singleton classes into this namespace
{
class MyClass
{
...
};
}
extern SmartSingleton<Singleton::MyClass> MyClass; //<-- add this to your singleton class header file
...
//-- MyFile.cpp --
...
SmartSingleton<Singleton::MyClass> MyClass; //<-- add this to your singleton class source file
...
Basically it just involves defining a SmartSingleton variable that will eventually hold
an instance of your class when you set "MyClass = new Singleton::MyClass();" or whatever.
Note that when you assign a singleton instance, it will intentionally generate an error
if you assign like "MyClass = pointerToSingletonInstance;" This is not allowed since the
SmartSingleton automatically deletes the instance, and you keeping a pointer of it is not only
useless but misleading (for example you might out of habit call "delete pointerToSingletonInstance;",
which will always cause your program to crash at some point).
Note that this is just an example implementation, so you can organize or use the SmartSingleton
variable however you like. For example, if you prefer to name the SmartSingleton variable with a
prefix or something to explicitly identify it is a singleton, this is your choice (and it would
eliminate the need to put the actual class implementation within a Singleton namespace to avoid
a clash).
That said, if you use the naming convention where class names are always capitalized, and
functions/variables always begin in lower case, there will never be any confusion with the example
technique above since C++ does not support "MyClass->functionCall();" and it could not possibly be
interpreted as anything but a singleton.
Advanced: Defining SMARTSINGLETON_STATIC (recommended method is to uncomment the appropriate line
in SmartSingleton.h) will set a variation of SmartSingleton to be used which allocates the singleton
instance statically (at compile time) rather than dynamically. This prevents the need
for your singletons to consume memory in the dynamic heap, which may be desirable.
The interface of the variation is identical to the standard SmartSingleton, so nothing else
needs to be changed to use it except your singleton class must have a default constructor.
It is recommended that the default constructor do little or nothing if you intend to
assign the singleton using "new MyClass()" to avoid double initialization. Just remember
that the class destructor (if any) must be able to run on even an empty object without
crashing.
If your singleton class contains a large amount of raw data like arrays (pointers to arrays
are fine of course), etc., it is recommended to avoid initializing it using "new MyClass()"
since assigning in this way requires that the data be copied from the given object into
static memory. Instead consider using an "initialize()" member function. However, in most
cases this will not be necessary.
*/
//----------------------------------------------------------------------------------------------
static void SmartSingleton_BadInstantiation()
{
throw std::logic_error("SmartSingleton error: Bad instantiation - you may only instantiate a singleton class once.");
/* If you program is crashing here, then somewhere in your code you are defining or instantiating
a singleton illegally (more than once). */
}
//#define SMARTSINGLETON_STATIC
#ifndef SMARTSINGLETON_STATIC
template <class T>
class SmartSingleton
{
private:
static T *instance;
public:
SmartSingleton()
{
if (instance != NULL)
SmartSingleton_BadInstantiation();
}
~SmartSingleton()
{
if (instance != NULL)
delete instance;
}
///See class documentation for examples of use of the "=" operator.
inline SmartSingleton& operator=(T *ptr)
{
if (ptr != NULL) {
if (instance != NULL)
SmartSingleton_BadInstantiation();
instance = ptr;
} else {
delete instance;
instance = ptr;
}
return *this;
}
//This should make it impossible to set to an lvalue, which is not allowed for safety
inline SmartSingleton& operator=(T *&ptr)
{
ptr = NULL;
throw std::logic_error("Internal error!"); //Function should never compile to execute
}
inline T& operator*() const { assert(instance); return *instance; }
inline T* operator->() const { assert(instance); return instance; }
bool operator==(const T *other) const { return (instance == other); }
bool operator!=(const T *other) const { return (instance != other); }
operator bool() { return (instance != NULL); }
};
template <class T> T *SmartSingleton<T>::instance = NULL;
#endif
#ifdef SMARTSINGLETON_STATIC
template <class T>
class SmartSingleton
{
private:
static T staticInstance;
static bool instantiated;
public:
SmartSingleton()
{
if (instantiated)
SmartSingleton_BadInstantiation();
}
~SmartSingleton()
{
staticInstance.~T();
staticInstance = T();
instantiated = false;
}
///See class documentation for examples of use of the "=" operator.
inline SmartSingleton& operator=(T *ptr)
{
if (ptr != NULL) {
if (instantiated)
SmartSingleton_BadInstantiation();
staticInstance = *ptr;
*ptr = T();
delete ptr;
instantiated = true;
} else {
staticInstance.~T();
staticInstance = T();
instantiated = false;
}
return *this;
}
//This should make it impossible to set to an lvalue, which is not allowed for safety
inline SmartSingleton& operator=(T *&ptr)
{
ptr = NULL;
throw std::logic_error("Internal error!"); //Function should never compile to execute
}
inline T& operator*() const { assert(instantiated); return staticInstance; }
inline T* operator->() const { assert(instantiated); return &staticInstance; }
bool operator==(const T *other) const { return (other == NULL ? !instantiated : instantiated); }
bool operator!=(const T *other) const { return (other != NULL ? !instantiated : instantiated); }
operator bool() { return instantiated; }
};
template <class T> T SmartSingleton<T>::staticInstance;
template <class T> bool SmartSingleton<T>::instantiated = false;
#endif
///This macro can be inserted at the top of your constructor to ensure that only one instance
///is ever created:
#define ENFORCE_SINGLETON_CONSTRUCTION \
static bool ____instantiated = false; \
if (____instantiated) SmartSingleton_BadInstantiation(); \
____instantiated = true;
#endif
