//////////////////////////////////////////////////////////////////////////////// // The Loki Library // Copyright (c) 2001 by Andrei Alexandrescu // This code accompanies the book: // Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design // Patterns Applied". Copyright (c) 2001. Addison-Wesley. // Permission to use, copy, modify, distribute and sell this software for any // purpose is hereby granted without fee, provided that the above copyright // notice appear in all copies and that both that copyright notice and this // permission notice appear in supporting documentation. // The author or Addison-Wesley Longman make no representations about the // suitability of this software for any purpose. It is provided "as is" // without express or implied warranty. //////////////////////////////////////////////////////////////////////////////// #ifndef LOKI_SINGLETON_INC_ #define LOKI_SINGLETON_INC_ // $Header$ #include "Threads.h" #include #include #include #include #include #include #include #include #ifdef _MSC_VER #define LOKI_C_CALLING_CONVENTION_QUALIFIER __cdecl #else #define LOKI_C_CALLING_CONVENTION_QUALIFIER #endif /// \defgroup SingletonGroup Singleton /// \defgroup CreationGroup Creation policies /// \ingroup SingletonGroup /// \defgroup LifetimeGroup Lifetime policies /// \ingroup SingletonGroup namespace Loki { typedef void (LOKI_C_CALLING_CONVENTION_QUALIFIER *atexit_pfn_t)(); namespace Private { void LOKI_C_CALLING_CONVENTION_QUALIFIER AtExitFn(); // declaration needed below class LifetimeTracker; #define LOKI_ENABLE_NEW_SETLONGLIVITY_HELPER_DATA_IMPL #ifdef LOKI_ENABLE_NEW_SETLONGLIVITY_HELPER_DATA_IMPL // Helper data // std::list because of the inserts typedef std::list TrackerArray; extern TrackerArray* pTrackerArray; #else // Helper data typedef LifetimeTracker** TrackerArray; extern TrackerArray pTrackerArray; extern unsigned int elements; #endif //////////////////////////////////////////////////////////////////////////////// // class LifetimeTracker // Helper class for SetLongevity //////////////////////////////////////////////////////////////////////////////// class LifetimeTracker { public: LifetimeTracker(unsigned int x) : longevity_(x) {} virtual ~LifetimeTracker() = 0; static bool Compare(const LifetimeTracker* lhs, const LifetimeTracker* rhs) { return lhs->longevity_ > rhs->longevity_; } private: unsigned int longevity_; }; // Definition required inline LifetimeTracker::~LifetimeTracker() {} // Helper destroyer function template struct Deleter { typedef void (*Type)(T*); static void Delete(T* pObj) { delete pObj; } }; // Concrete lifetime tracker for objects of type T template class ConcreteLifetimeTracker : public LifetimeTracker { public: ConcreteLifetimeTracker(T* p,unsigned int longevity, Destroyer d) : LifetimeTracker(longevity) , pTracked_(p) , destroyer_(d) {} ~ConcreteLifetimeTracker() { destroyer_(pTracked_); } private: T* pTracked_; Destroyer destroyer_; }; } // namespace Private //////////////////////////////////////////////////////////////////////////////// /// \ingroup LifetimeGroup /// /// Assigns an object a longevity; ensures ordered destructions of objects /// registered thusly during the exit sequence of the application //////////////////////////////////////////////////////////////////////////////// #ifdef LOKI_ENABLE_NEW_SETLONGLIVITY_HELPER_DATA_IMPL template void SetLongevity(T* pDynObject, unsigned int longevity, Destroyer d) { using namespace Private; // manage lifetime of stack manually if(pTrackerArray==0) pTrackerArray = new TrackerArray; // automatically delete the ConcreteLifetimeTracker object when a exception is thrown std::auto_ptr p( new ConcreteLifetimeTracker(pDynObject, longevity, d) ); // Find correct position TrackerArray::iterator pos = std::upper_bound( pTrackerArray->begin(), pTrackerArray->end(), p.get(), LifetimeTracker::Compare); // Insert the pointer to the ConcreteLifetimeTracker object into the queue pTrackerArray->insert(pos, p.get()); // nothing has thrown: don't delete the ConcreteLifetimeTracker object p.release(); // Register a call to AtExitFn std::atexit(Private::AtExitFn); } #else template void SetLongevity(T* pDynObject, unsigned int longevity, Destroyer d) { using namespace Private; TrackerArray pNewArray = static_cast( std::realloc(pTrackerArray, sizeof(*pTrackerArray) * (elements + 1))); if (!pNewArray) throw std::bad_alloc(); // Delayed assignment for exception safety pTrackerArray = pNewArray; LifetimeTracker* p = new ConcreteLifetimeTracker( pDynObject, longevity, d); // Insert a pointer to the object into the queue TrackerArray pos = std::upper_bound( pTrackerArray, pTrackerArray + elements, p, LifetimeTracker::Compare); std::copy_backward( pos, pTrackerArray + elements, pTrackerArray + elements + 1); *pos = p; ++elements; // Register a call to AtExitFn std::atexit(Private::AtExitFn); } #endif template void SetLongevity(T* pDynObject, unsigned int longevity, typename Private::Deleter::Type d = Private::Deleter::Delete) { SetLongevity::Type>(pDynObject, longevity, d); } //////////////////////////////////////////////////////////////////////////////// /// \struct CreateUsingNew /// /// \ingroup CreationGroup /// Implementation of the CreationPolicy used by SingletonHolder /// Creates objects using a straight call to the new operator //////////////////////////////////////////////////////////////////////////////// template struct CreateUsingNew { static T* Create() { return new T; } static void Destroy(T* p) { delete p; } }; //////////////////////////////////////////////////////////////////////////////// /// \struct CreateUsing /// /// \ingroup CreationGroup /// Implementation of the CreationPolicy used by SingletonHolder /// Creates objects using a custom allocater. /// Usage: e.g. CreateUsing::Allocator //////////////////////////////////////////////////////////////////////////////// template class Alloc> struct CreateUsing { template struct Allocator { static Alloc allocator; static T* Create() { return new (allocator.allocate(1)) T; } static void Destroy(T* p) { //allocator.destroy(p); p->~T(); allocator.deallocate(p,1); } }; }; //////////////////////////////////////////////////////////////////////////////// /// \struct CreateUsingMalloc /// /// \ingroup CreationGroup /// Implementation of the CreationPolicy used by SingletonHolder /// Creates objects using a call to std::malloc, followed by a call to the /// placement new operator //////////////////////////////////////////////////////////////////////////////// template struct CreateUsingMalloc { static T* Create() { void* p = std::malloc(sizeof(T)); if (!p) return 0; return new(p) T; } static void Destroy(T* p) { p->~T(); std::free(p); } }; //////////////////////////////////////////////////////////////////////////////// /// \struct CreateStatic /// /// \ingroup CreationGroup /// Implementation of the CreationPolicy used by SingletonHolder /// Creates an object in static memory /// Implementation is slightly nonportable because it uses the MaxAlign trick /// (an union of all types to ensure proper memory alignment). This trick is /// nonportable in theory but highly portable in practice. //////////////////////////////////////////////////////////////////////////////// template struct CreateStatic { #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4121 ) // alignment of a member was sensitive to packing #endif // _MSC_VER union MaxAlign { char t_[sizeof(T)]; short int shortInt_; int int_; long int longInt_; float float_; double double_; long double longDouble_; struct Test; int Test::* pMember_; int (Test::*pMemberFn_)(int); }; #ifdef _MSC_VER #pragma warning( pop ) #endif // _MSC_VER static T* Create() { static MaxAlign staticMemory_; return new(&staticMemory_) T; } static void Destroy(T* p) { p->~T(); } }; //////////////////////////////////////////////////////////////////////////////// /// \struct DefaultLifetime /// /// \ingroup LifetimeGroup /// Implementation of the LifetimePolicy used by SingletonHolder /// Schedules an object's destruction as per C++ rules /// Forwards to std::atexit //////////////////////////////////////////////////////////////////////////////// template struct DefaultLifetime { static void ScheduleDestruction(T*, atexit_pfn_t pFun) { std::atexit(pFun); } static void OnDeadReference() { throw std::logic_error("Dead Reference Detected"); } }; //////////////////////////////////////////////////////////////////////////////// /// \struct PhoenixSingleton /// /// \ingroup LifetimeGroup /// Implementation of the LifetimePolicy used by SingletonHolder /// Schedules an object's destruction as per C++ rules, and it allows object /// recreation by not throwing an exception from OnDeadReference //////////////////////////////////////////////////////////////////////////////// template class PhoenixSingleton { public: static void ScheduleDestruction(T*, atexit_pfn_t pFun) { #ifndef ATEXIT_FIXED if (!destroyedOnce_) #endif std::atexit(pFun); } static void OnDeadReference() { #ifndef ATEXIT_FIXED destroyedOnce_ = true; #endif } private: #ifndef ATEXIT_FIXED static bool destroyedOnce_; #endif }; #ifndef ATEXIT_FIXED template bool PhoenixSingleton::destroyedOnce_ = false; #endif //////////////////////////////////////////////////////////////////////////////// // Copyright (c) 2004 by Curtis Krauskopf - curtis@decompile.com /// /// \struct DeletableSingleton /// /// \ingroup LifetimeGroup /// /// A DeletableSingleton allows the instantiated singleton to be /// destroyed at any time. The singleton can be reinstantiated at /// any time, even during program termination. /// If the singleton exists when the program terminates, it will /// be automatically deleted. /// /// \par Usage: /// The singleton can be deleted manually: /// /// DeletableSingleton::GracefulDelete(); //////////////////////////////////////////////////////////////////////////////// template class DeletableSingleton { public: static void ScheduleDestruction(T*, atexit_pfn_t pFun) { static bool firstPass = true; isDead = false; deleter = pFun; if (firstPass || needCallback) { std::atexit(atexitCallback); firstPass = false; needCallback = false; } } static void OnDeadReference() { } /// delete singleton object manually static void GracefulDelete() { if (isDead) return; isDead = true; deleter(); } protected: static atexit_pfn_t deleter; static bool isDead; static bool needCallback; static void atexitCallback() { #ifdef ATEXIT_FIXED needCallback = true; #else needCallback = false; #endif GracefulDelete(); } }; template atexit_pfn_t DeletableSingleton::deleter = 0; template bool DeletableSingleton::isDead = true; template bool DeletableSingleton::needCallback = true; //////////////////////////////////////////////////////////////////////////////// // class template Adapter // Helper for SingletonWithLongevity below //////////////////////////////////////////////////////////////////////////////// namespace Private { template struct Adapter { void operator()(T*) { return pFun_(); } atexit_pfn_t pFun_; }; } //////////////////////////////////////////////////////////////////////////////// /// \struct SingletonWithLongevity /// /// \ingroup LifetimeGroup /// Implementation of the LifetimePolicy used by SingletonHolder /// Schedules an object's destruction in order of their longevities /// Assumes a visible function GetLongevity(T*) that returns the longevity of the /// object. //////////////////////////////////////////////////////////////////////////////// template class SingletonWithLongevity { public: static void ScheduleDestruction(T* pObj, atexit_pfn_t pFun) { Private::Adapter adapter = { pFun }; SetLongevity(pObj, GetLongevity(pObj), adapter); } static void OnDeadReference() { throw std::logic_error("Dead Reference Detected"); } }; //////////////////////////////////////////////////////////////////////////////// /// \struct NoDestroy /// /// \ingroup LifetimeGroup /// Implementation of the LifetimePolicy used by SingletonHolder /// Never destroys the object //////////////////////////////////////////////////////////////////////////////// template struct NoDestroy { static void ScheduleDestruction(T*, atexit_pfn_t) {} static void OnDeadReference() {} }; //////////////////////////////////////////////////////////////////////////////// /// \defgroup LongevityLifetimeGroup LongevityLifetime /// \ingroup LifetimeGroup /// /// \namespace LongevityLifetime /// /// \ingroup LongevityLifetimeGroup /// \brief In this namespace are special lifetime policies to manage lifetime /// dependencies. //////////////////////////////////////////////////////////////////////////////// namespace LongevityLifetime { //////////////////////////////////////////////////////////////////////////////// /// \struct SingletonFixedLongevity /// /// \ingroup LongevityLifetimeGroup /// Add your own lifetimes into the namespace 'LongevityLifetime' /// with your prefered lifetime by adding a struct like this: /// /// template /// struct MyLifetime : SingletonFixedLongevity< MyLifetimeNumber ,T> {} //////////////////////////////////////////////////////////////////////////////// template class SingletonFixedLongevity { public: virtual ~SingletonFixedLongevity() {} static void ScheduleDestruction(T* pObj, atexit_pfn_t pFun) { Private::Adapter adapter = { pFun }; SetLongevity(pObj, Longevity , adapter); } static void OnDeadReference() { throw std::logic_error("Dead Reference Detected"); } }; /// \struct DieLast /// \ingroup LongevityLifetimeGroup /// \brief Longest possible SingletonWithLongevity lifetime: 0xFFFFFFFF template struct DieLast : SingletonFixedLongevity<0xFFFFFFFF ,T> {}; /// \struct DieDirectlyBeforeLast /// \ingroup LongevityLifetimeGroup /// \brief Lifetime is a one less than DieLast: 0xFFFFFFFF-1 template struct DieDirectlyBeforeLast : SingletonFixedLongevity<0xFFFFFFFF-1 ,T> {}; /// \struct DieFirst /// \ingroup LongevityLifetimeGroup /// \brief Shortest possible SingletonWithLongevity lifetime: 0 template struct DieFirst : SingletonFixedLongevity<0,T> {}; }//namespace LongevityLifetime //////////////////////////////////////////////////////////////////////////////// /// \class FollowIntoDeath /// /// \ingroup LifetimeGroup /// /// Lifetime policyfor the SingletonHolder tempalte. /// Followers will die after the master dies Followers will not die, if /// - master never dies (NoDestroy policy) /// - master never created /// - master dies not in the function registered with atexit /// - master dies not by a call of a the atexit registerd function (DeletableSingleton::GracefulDelete) /// /// \par Usage: /// /// Lifetimes of the master and the follower singletons, e.g. with a M and a F class: /// \code SingletonHolder< M , FollowIntoDeath::With::AsMasterLifetime > MasterSingleton; \endcode /// \code SingletonHolder< F , CreateUsingNew, FollowIntoDeath::AfterMaster< MasterSingleton >::IsDestroyed > FollowerSingleton \endcode //////////////////////////////////////////////////////////////////////////////// class FollowIntoDeath { template class Followers { typedef std::vector Container; typedef typename Container::iterator iterator; static Container* followers_; public: static void Init() { static bool done = false; if(!done) { followers_ = new Container; done = true; } } static void AddFollower(atexit_pfn_t ae) { Init(); followers_->push_back(ae); } static void DestroyFollowers() { Init(); for(iterator it = followers_->begin();it != followers_->end();++it) (*it)(); delete followers_; } }; public: /// \struct With /// Template for the master /// \param Lifetime Lifetime policy for the master template