I am stuck with a problem where I want to create a templatized singleton object. Something like below
template<class T>
class Singleton {
public:
static Singleton<T>& GetInstance()
{
static Singleton<T> singleton;
return singleton;
}
void Register(T Pointer) {/* */}
void DoWork() { /* */ }
private:
Singleton() {}
std::unordered_set<T> pointers;
};
Next goal is to get access object say through a global templatized function like this
template<class T>
void Register(T Pointer) {
Singleton<T>::GetInstance().Register(Pointer);
}
Is there a way that for a single class Hierarchy I create one singleton object. For something like below Base and derived types both result in same singleton object.
class Base{
};
class Derived : public Base {
};
One solution is to introduce a new base class to your heiratchy that handles the registration and rely on CRTP to ensure that the instances are registered with the correct singleton. First we'll clean up the Singleton itself so that the Register takes a pointer instead of a plain type of T.
template<class T>
class Singleton {
public:
static Singleton<T>& GetInstance()
{
static Singleton<T> singleton;
return singleton;
}
void Register(T* /*Pointer*/) {/* */ }
};
Now we'll introduce a new templated base class with a single type argument which handles the registration with the singleton based on the type it's passed. This will be used as the base class for Base (in your sample code).
template<class T>
struct Registerable
{
virtual ~Registerable() = default;
virtual void Register() final
{
std::cout
<< "Singleton @ "
<< &Singleton<T>::GetInstance()
<< "\n";
Singleton<T>::GetInstance().Register(static_cast<T*>(this));
}
};
I've marked the Register function as virtual and final to prevent derived classes from overriding it. I've also added a single log to cout so that we can track which singleton is being used for registration. Now all that we need to do is have Base derive from Registerable and pass Base as the template argument (CRTP).
struct Base : Registerable<Base>
{
// ....
};
The classes that derive from Base remain unchanged as there's nothing extra for them to do.
struct Derived : public Base {};
struct OtherDerived : public Derived {};
And now we'll run a little test code to see what happens.
int main()
{
Base base;
Derived derived;
OtherDerived otherDerived;
base.Register();
derived.Register();
otherDerived.Register();
}
And our output is:
Singleton @ 0015C75C
Singleton @ 0015C75C
Singleton @ 0015C75C
I don't have a lot of details on how you plan to use this so how you mold it to what you're trying to accomplish is up to you.
Note that you also have the option of making Registerable an inner class of Singleton. This allows you make the Register function in Singleton private in order to prevent it from being directly used.
template<class T>
class Singleton {
public:
struct Registerable
{
virtual ~Registerable() = default;
virtual void Register() final
{
std::cout
<< "Singleton @ "
<< &Singleton<T>::GetInstance()
<< "\n";
Singleton<T>::GetInstance().Register(static_cast<T*>(this));
}
};
static Singleton<T>& GetInstance()
{
static Singleton<T> singleton;
return singleton;
}
private:
void Register(T* /*Pointer*/) {/* */ }
};
If you do this the Base class changes only slightly.
struct Base : Singleton<Base>::Registerable
{
// ....
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With