Reputation: 526
I've been trying to find a singleton implementation where:
Application
which is abstractApplication
, for example SampleApplication
and OtherSampleApplication
.Application
, whatever the actual type is, so I've been trying to make it a singleton.Here's my current Singleton implementation:
template<class T>
class Singleton
{
public:
static T& GetInstance()
{
static T instance;
return instance;
}
Singleton(Singleton const&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton const&) = delete;
Singleton& operator=(Singleton&&) = delete;
protected:
Singleton() = default;
virtual ~Singleton() = default;
};
Then, for the derived classes, I've tried a number of things:
class Application : public Singleton<Application>
with class SampleApplication : public Application
Application
instance instead of a SampleApplication
.class Application : public Singleton<Application>
with class SampleApplication : public Application, public Singleton<SampleApplication>
GetInstance()
is ambiguous. I tried to work around this with virtual
inheritance but couldn't get it to compile.class Application
with class SampleApplication : public Application, public Singleton<SampleApplication>
Does anyone know a way to accomplish this?
Upvotes: 4
Views: 793
Reputation: 17082
This is an approach that will produce compile-time errors if there is more than one class derived from Application
. Usually. It is not fool-proof, and it does have drawbacks. I think it is worth considering.
Given the class name "Application", I guess that this is potentially part of a library. If the library is not header-only, there is an extra consideration that I'll address that at the end.
The overall picture for this approach is simple inheritance, plus an extra step for whomever defines a class derived from Application
.
class Application : public Singleton<Application> { /* ... */ };
class SampleApplication : public Application { /* ... */ };
REGISTER_APPLICATION(SampleApplication) // Must not be in a header
The last line will be explained later.
First, let's hide Singleton<Application>::GetInstance()
, which will not compile since Application
is abstract. (I am assuming that everyone will type the shorter Application::GetInstance()
to use this function; if not, this approach is scrap.) Add the function declaration to the abstract class, but do not add a definition yet.
class Application : public Singleton<Application> {
public:
static Application & GetInstance();
// ...
};
The definition will be added elsewhere, not in a header file. Taking the definition out of header files is the reason for hiding the function in the Singleton
template. (While the extern
keyword allows moving some template definitions out of header files, my understanding is that it does not fully suppress an inline definition when there is one, as in this case.) Where will the definition of GetInstance()
live? That's where the extra step comes in. It's also where I could get blasted because Macros Are Evil unless they are needed. It's needed. Put the definition of this function in a macro.
#define REGISTER_APPLICATION(name) \
Application& Application::GetInstance() \
{ \
static name instance; \
return instance; \
}
Now document that when deriving from Application
, this macro must be used in a source file, not a header file. The parameter to the macro is the name of the derived class; if the derived class is SampleApplication
then the line to add to the source file is REGISTER_APPLICATION(SampleApplication)
.
If the macro is not used, the linker will complain about the undefined reference to Application::GetInstance()
. Unfortunately, this is not as clear about the problem as I would like. Probably put an entry in your framework's FAQ covering this.
If the macro is used twice, the linker will complain about multiple definitions of Application::GetInstance()
. This is why the definition cannot be given inline. We want the one definition rule to apply. This is not fool-proof (maybe there are two classes derived from Application
and only one remembered the macro), but it might be adequate.
For a library where parts of Application
are defined outside headers, there would be a problem in that Application::GetInstance()
cannot be part of the library, leading to an undefined reference when compiling the library. One solution for that is to make Application
a wrapper for another class that is supposed to be internal to the library. For example:
class Application : public InternalApplication, public Singleton<Application> {
static Application & GetInstance();
// Everything else is inherited from InternalApplication.
};
Now Application
can be header-only, while InternalApplication
can have components in a static (or dynamic) library.
Upvotes: 1
Reputation: 13120
This solution shows how only allowing one instance based on a baseclass can work:
#include <iostream>
#include <type_traits>
//---------------------------------------------------------------------------------------------
// type for checking if something is a singleton later
struct SingletonBase
{
};
//---------------------------------------------------------------------------------------------
// specialized singleton to be used in baseclass (Application)
// keeps track of whether an instance of the baseclass has already been made
template<typename T>
struct Singleton :
public SingletonBase
{
Singleton()
{
has_instance = true;
}
static bool has_instance;
};
// has_instance initilization
template<typename T>
bool Singleton<T>::has_instance = false;
//---------------------------------------------------------------------------------------------
// Baseclass for applications.
class Application :
public Singleton<Application>
{
protected:
Application() = default; // do not allow client code to make instance of base class
};
class SomeOtherKindOfApplication :
public Singleton<SomeOtherKindOfApplication>
{
protected:
SomeOtherKindOfApplication() = default;
};
//---------------------------------------------------------------------------------------------
// application types derived from Application and SomeOtherApplication
class Application1 :
public Application
{
public:
void Hello()
{
std::cout << "Hello, " << std::endl;
}
};
class Application2 :
public Application
{
public:
void World()
{
std::cout << "World !" << std::endl;
}
};
class SomeOtherKindOfApplication1 :
public SomeOtherKindOfApplication
{
public:
void Bye()
{
std::cout << "Bye!" << std::endl;
}
};
class NotASingleton
{
};
//---------------------------------------------------------------------------------------------
// Singleton instance getter
template<typename T>
static T& GetInstance()
{
static_assert(std::is_base_of_v<SingletonBase, T>, "Can only create instances of classes derived from singleton");
if (T::has_instance)
{
throw std::runtime_error("An application instance has already been made");
}
static T instance;
return instance;
}
//---------------------------------------------------------------------------------------------
int main()
{
auto instance = GetInstance<Application1>();
instance.Hello();
try
{
// getting an instance of another application derived
// from Application will fail (runtime)
auto instance2 = GetInstance<Application2>();
instance2.World();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
// but getting application with another baseclass should be fine
auto instance3 = GetInstance<SomeOtherKindOfApplication1>();
instance3.Bye();
// next line will not even compile, NotASingleton isn't a singleton
// auto no_instance = GetInstance<NotASingleton>();
}
Upvotes: 1
Reputation: 118445
Since you mentioned that you will settle for a runtime error:
template<class T>
class Singleton : private ReallySingleton {
The rest of the singleton is as is. The private base class will go something like this:
class ReallySingleton {
static int counter=0;
protected:
ReallySingleton()
{
if (++counter > 1)
throw std::runtime_error{"Too many singletons"};
}
};
(ReallySingleton::counter
will need to be defined somewhere where that's convenient)
Upvotes: 1