Created
May 22, 2018 18:58
-
-
Save langthom/4a85e9ded0fc3a03416239fc45dd6108 to your computer and use it in GitHub Desktop.
Singleton design pattern implementation as presented by Andrei Alexandrescu in his book "Modern C++ Design".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Singleton pattern as described by Andrei Alexandrescu in "Modern C++ Design" | |
#include <cassert> | |
#include <cstdlib> | |
#include <iostream> | |
//////////////////////////////////////////////////////////////////////////////// | |
namespace singleton { | |
/// Singleton implementation. | |
namespace defaultPolicies { | |
/// Default policies. | |
/// \class CreateUsingNew | |
/// \brief Creates a new instance of \a Type using the \a new operator. | |
/// \tparam Type Type to create an instance from. Must be default-constructible. | |
template<typename Type> | |
class CreateUsingNew { | |
public: | |
/// Creates a new instance and returns a pointer to it. | |
/// \return Returns the newly created instance. | |
static inline Type* Create() { return new Type; } | |
/// Destroys the passed instance. | |
/// \param obj The instance to destroy. | |
static inline void Destroy(Type* obj) { delete obj; } | |
}; | |
/// \class DefaultLifetime | |
/// \brief Implements object lifetime per C++ rules: Last created - first destroyed. | |
/// \tparam Type Type of the object to track liftime of. Not used here. | |
template<typename Type> | |
class DefaultLifetime { | |
public: | |
/// Handler if dead reference is detected in the singleton. | |
/// \throws Throws a \a std::logic_error. | |
static inline void OnDeadReference() { throw std::logic_error("Dead reference!"); } | |
/// Schedules destruction of the singleton. | |
/// This policy uses \a std::exit to implement object lifetime according to C++ rules. | |
/// \param fun Function that destroys the singleton. | |
static inline void ScheduleDestruction(void (*fun)()) { std::atexit(fun); } | |
}; | |
/// \class SingleThreaded | |
/// \brief Policy class only allowing single threaded application. Minimalistic here. | |
/// \tparam Type Type of the instance that can be accessed in parallel. | |
template<typename Type> | |
class SingleThreaded { | |
/// \struct DummyLock | |
/// \brief Dummy lock instance, does not lock. | |
struct DummyLock {}; | |
public: | |
typedef DummyLock Lock; ///< Lock guard type. | |
typedef Type VolatileType; ///< Volatile type. Not volatile here to allow better optimizations. | |
}; | |
} // defaultPolicies | |
/// \class Singleton | |
/// \brief Class implementing the \a Singleton design pattern for a passed type. | |
/// | |
/// This class implements the \a Singleton design pattern which guarantees that there is at most | |
/// one instance of the passed instance type kept alive. | |
/// | |
/// This class accepts three different policies: | |
/// | |
/// - CreationPolicy: | |
/// - Policy for creating and destroying objects. | |
/// - Interface: | |
/// - InstanceT* Create() | |
/// - void Destroy(InstanceT* obj) | |
/// - Defaults to: singleton::defaultPolicies::CreateUsingNew | |
/// - LifetimePolicy: | |
/// - Policy for managing the lifetime of objects. | |
/// - Interface: | |
/// - void OnDeadReference() | |
/// - void ScheduleDestruction(void (*)()) | |
/// - Defaults to: singleton::defaultPolicies::DefaultLifetime | |
/// - ThreadingModel: | |
/// - Policy for parallel access protection. | |
/// - Needed typedefs: | |
/// - Lock | |
/// - VolatileType | |
/// - Defaults to: singleton::defaultPolicies::SingleThreaded | |
/// | |
/// Example usage: | |
/// | |
/// class A {}; | |
/// | |
/// A& ins = Singleton<A>::Instance(); | |
/// A& oth = Singleton<A>::Instance(); | |
/// assert(std::addressof(ins) == std::addressof(oth)); | |
/// | |
/// \tparam InstanceT Type to contain a single instance of. | |
/// \tparam CreationPolicy Policy responsible for creating and destroying instances of \a InstanceT. | |
/// \tparam LifetimePolicy Policy responsible for tracking the lifetime of the instance. | |
/// \tparam ThreadingModel Policy responsible for protecting the instance from parallel access. | |
/// | |
/// \author Andrei Alexandrescu (in "Modern C++ Design"), documented by Thomas Lang. | |
/// \date 2018-05-22 | |
template< | |
typename InstanceT, | |
template <typename> class CreationPolicy = defaultPolicies::CreateUsingNew, | |
template <typename> class LifetimePolicy = defaultPolicies::DefaultLifetime, | |
template <typename> class ThreadingModel = defaultPolicies::SingleThreaded | |
> | |
class Singleton { | |
public: | |
/// Gets the instance held by the singleton. Initializes it on first call. | |
/// \return Returns the kept instance. | |
static InstanceT& Instance(); | |
private: | |
/// Destroys the Singleton. | |
static void DestroySingleton(); | |
// Prevents the user from creating, copying, assigning or destroying the instance. | |
Singleton() = delete; | |
Singleton(const Singleton&) = delete; | |
Singleton& operator=(const Singleton&) = delete; | |
~Singleton() = delete; | |
private: | |
typedef typename ThreadingModel<InstanceT>::VolatileType InstanceType; ///< Instance type, maybe volatile. | |
static InstanceType* instance; ///< Static, sole instance. | |
static bool destroyed; ///< Indicator if the Singleton was destroyed. | |
}; | |
/// Static member initialization. | |
template< | |
typename InstanceT, | |
template <typename> class CreationPolicy, | |
template <typename> class LifetimePolicy, | |
template <typename> class ThreadingModel | |
> | |
typename Singleton<InstanceT, CreationPolicy, LifetimePolicy, ThreadingModel>::InstanceType* | |
Singleton<InstanceT, CreationPolicy, LifetimePolicy, ThreadingModel>::instance = nullptr; | |
template< | |
typename InstanceT, | |
template <typename> class CreationPolicy, | |
template <typename> class LifetimePolicy, | |
template <typename> class ThreadingModel | |
> | |
bool Singleton<InstanceT, CreationPolicy, LifetimePolicy, ThreadingModel>::destroyed = false; | |
//////////////////////////////////////////////////////////////////////////////// | |
template< | |
typename InstanceT, | |
template <typename> class CreationPolicy, | |
template <typename> class LifetimePolicy, | |
template <typename> class ThreadingModel | |
> | |
InstanceT& Singleton<InstanceT, CreationPolicy, LifetimePolicy, ThreadingModel>::Instance() { | |
if (!instance) { | |
typename ThreadingModel<InstanceT>::Lock guard; | |
(void) guard; // Suppresses the "unused variable" warning. | |
// The famous double-lock-checking model: | |
// Check in a possibly multi-threaded environment if it is initialized at all. | |
// If not, lock the access to it. | |
// Then, check again. Because it is locked by now and if it was not created yet, | |
// no other thread can create it in parallel now. We can safely create it then. | |
if (!instance) { | |
if (destroyed) { | |
// If the instance is not present and we know that it was already destroyed, | |
// something bad happened. Let the LifetimePolicy decide what to do now. | |
LifetimePolicy<InstanceT>::OnDeadReference(); | |
destroyed = false; | |
} | |
// Now, let the CreationPolicy create the instance and schedule its destruction | |
// according to the Lifetime policy. | |
instance = CreationPolicy<InstanceT>::Create(); | |
LifetimePolicy<InstanceT>::ScheduleDestruction(&DestroySingleton); | |
} | |
} | |
return *instance; | |
} | |
template< | |
typename InstanceT, | |
template <typename> class CreationPolicy, | |
template <typename> class LifetimePolicy, | |
template <typename> class ThreadingModel | |
> | |
void Singleton<InstanceT, CreationPolicy, LifetimePolicy, ThreadingModel>::DestroySingleton() { | |
assert(!destroyed && "Singleton was already destroyed!"); | |
// Destroying is simple: Destroy the instance and clean up. | |
CreationPolicy<InstanceT>::Destroy(instance); | |
instance = nullptr; | |
destroyed = true; | |
} | |
} // singleton | |
//////////////////////////////////////////////////////////////////////////////// | |
// Test: | |
class A { | |
public: | |
A() : count(0) {} | |
inline void inc() { ++count; } | |
inline unsigned int get() const { return count; } | |
private: | |
unsigned int count; | |
}; | |
int main(void) { | |
A& singletonOfA = singleton::Singleton<A>::Instance(); | |
singletonOfA.inc(); | |
A& anotherSingleton = singleton::Singleton<A>::Instance(); | |
anotherSingleton.inc(); | |
// Make sure that it's really the same instance. | |
std::cout << "Instances equal? " << std::boolalpha | |
<< (std::addressof(singletonOfA) == std::addressof(anotherSingleton)) | |
<< std::endl; | |
std::cout << "anotherSingleton.get() == " | |
<< anotherSingleton.get() | |
<< " [expected 2]" << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment