LifeTracker.h
#ifndef _LIFETRACKER_H_
#define _LIFETRACKER_H_
#include <vector>
#include <algorithm>
#include "MySIngleton.h"
class LifetimeTracker
{
public:
LifetimeTracker(unsigned int x) : longevity_(x) {}
virtual ~LifetimeTracker() = 0;
friend bool Compare(const LifetimeTracker* p, const LifetimeTracker* q);
void setKey(const std::string& key)
{
key_ = key;
}
const std::string& getKey()
{
return key_;
}
void setLongevity(unsigned int nlongevity)
{
longevity_ = nlongevity;
}
private:
//This is the relative age of Singleton object(s)
unsigned int longevity_;
//Stored this Key to Identify Singleton Objects, if client want to Set Longivity
std::string key_;
};
inline bool Compare(const LifetimeTracker* p, const LifetimeTracker* q)
{
return p->longevity_ < q->longevity_;
}
// Definition required
LifetimeTracker::~LifetimeTracker() = default;
//Helper destroyer function
// Concrete lifetime tracker for objects of type T
template <typename T, typename Destroyer>
class ConcreteLifetimeTracker : public LifetimeTracker
{
public:
ConcreteLifetimeTracker(T* p, unsigned int longevity, Destroyer d) : LifetimeTracker(longevity)
, pTracked_(p)
, destroyer_(d)
{
}
~ConcreteLifetimeTracker()
{
destroyer_(pTracked_);
}
private:
//This is the tracked object
T* pTracked_;
//This is destroying method
Destroyer destroyer_;
};
//This will be given to std::atexit
void AtExitFn(); // Declaration needed below
template <typename T>
struct Deleter;
template <typename T, typename D >
class Singleton;
//This is generic deleter, but client can also give its deleter
template <typename T>
struct Deleter
{
void operator()(T* pObj)
{
Singleton<T, Deleter<T> >::Destroy(pObj);
}
};
template <typename T, typename D>
std::string getObjectKey()
{
std::string key = typeid(T).name();
key = key + typeid(D).name();
return key;
}
typedef std::vector<LifetimeTracker*> TrackerArray;
TrackerArray pTrackerArray;
unsigned int Priority = 0;
template <typename T, typename Destroyer >
int SetLongevity(T* pobj, Destroyer d, unsigned int nlogevity = 0)
{
if (nlogevity == 0)
{
nlogevity = ++Priority;
}
LifetimeTracker* ptracker = new ConcreteLifetimeTracker<T, Destroyer>(pobj, nlogevity, d);
ptracker->setKey(getObjectKey<T, Destroyer>());
pTrackerArray.push_back(ptracker);
return 1;
}
template <typename T, typename Destroyer = Deleter<T> >
void SetLongevity(unsigned int nlogevity)
{
for (auto i : pTrackerArray)
{
if (i->getKey() == getObjectKey<T, Destroyer>())
{
i->setLongevity(nlogevity);
}
}
}
void AtExitFn()
{
sort(pTrackerArray.begin(), pTrackerArray.end(), Compare);
for (auto i : pTrackerArray)
{
delete *&i;
}
pTrackerArray.erase(pTrackerArray.begin(), pTrackerArray.end());
}
#endif // !_LIFETRACER_H
MySIngleton.h
#ifndef _MYSINGLETON_H_
#define _MYSINGLETON_H_
#include "LifeTracker.h"
#include <string>
//If client Inherit from this class, it needs not to private or delete it all construc operations
class NoConstructOperation
{
protected:
NoConstructOperation() = default;
virtual ~NoConstructOperation() = default;
public:
NoConstructOperation(const NoConstructOperation&) = delete;
NoConstructOperation& operator =(NoConstructOperation&) = delete;
NoConstructOperation(NoConstructOperation&&) = delete;
NoConstructOperation& operator = (NoConstructOperation&&) = delete;
};
// If client call Singleton<Myclass>, Singleton<Myclass, Deleter> it will give two different instance of Myclass
template
<
typename T, typename D = Deleter<T>
>
class Singleton : public NoConstructOperation
{
public:
//Check availabily of construcor, copy/move construction/assignment
//{
static_assert(!std::is_constructible<T>::value, "Constructor is public");
static_assert(!std::is_trivially_constructible<T>::value, "Compiler provided default constructor is public: Please declare it private");
static_assert(!std::is_trivially_copyable<T>::value, "Compiler provided copy constructor is avaliable");
static_assert(!std::is_copy_constructible<T>::value, "copy constructor is avaliable");
static_assert(!std::is_trivially_copy_assignable<T>::value, "Compiler provided assignment is avaliable");
static_assert(!std::is_copy_assignable<T>::value, "copy assisgnment is avaliable");
static_assert(!std::is_trivially_move_assignable<T>::value, "Compiler provided move assisgnment is avaliable");
static_assert(!std::is_move_assignable<T>::value, "move assisgnment is avaliable");
static_assert(!std::is_move_constructible<T>::value, "move constructor is avaliable");
static_assert(!std::is_trivially_move_constructible<T>::value, "Compiler provided move constructor is avaliable");
//}
static T& Instance();
static void Destroy(T* obj);
private:
static T* CreateInstance();
static int ScheduleForDestruction(void(*)());
};
template<typename T, typename D>
T& Singleton<T, D>::Instance()
{
//This will be thread safe ?
static T* ptr = CreateInstance();
// I have overloked it, this is for thread safety
static int i = SetLongevity<T, D>(ptr, D());
static int i = ScheduleForDestruction(&AtExitFn);
return *ptr;
}
template<typename T, typename D>
inline T* Singleton<T, D>::CreateInstance()
{
return new T();
}
template<typename T, typename D>
inline int Singleton<T, D>::ScheduleForDestruction(void(*pFun)())
{
std::atexit(pFun);
return 1;
}
template<typename T, typename D>
inline void Singleton<T, D>::Destroy(T* obj)
{
delete obj;
obj = nullptr;
}
#endif
Client Code
// C++11/14 thread safe singleton
#include "stdafx.h"
#include <typeinfo>
#include <stdexcept>
#include <stdlib.h>
#include <exception>
#include <string>
#include <sstream>
#include <iostream>
#include <atomic>
#include <cstdio>
#include "MySIngleton.h"
class Deleter1;
class Myclass : public NoConstructOperation
{
public:
friend class Deleter1;
friend class Singleton<Myclass>;
friend class Singleton<Myclass, Deleter1>;
private:
Myclass() = default;
~Myclass() = default;
};
class Deleter1
{
public:
void operator()(Myclass *p)
{
delete p;
p = nullptr;
}
};
class Myclass1 :public NoConstructOperation
{
public:
friend Singleton<Myclass1>;
};
int main()
{
Singleton<Myclass, Deleter1>::Instance();
SetLongevity<Myclass, Deleter1>(6);
Singleton<Myclass>::Instance();
getchar();
return 0;
}
This is Thread safe singleton. LifeTracker - will track the lifespan of Singleton Objects through longivity variable and through std::atexit, we will clean Singletons.
Singleton - Which has class of which instance will be created + a destroyer which if client does not provide , Single will clean by itself.
Pleae ask more details if you require to understand it.
#ifndef/#define
symbol unique - e.g by adding a long random suffix to it - to prevent collisions. \$\endgroup\$A
andB
and client want toA
live longer thanB
. He can call setLongivity function passing greater interger for A. Anyhow I am wondering @LokiAstari why atexit makes problem harder \$\endgroup\$