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).
So what happens when someone messes up?
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.
SampleApplicationclass:public: using Singleton<SampleApplication>::GetInstance;Singleton<Application>, andmainpasses into the that the derived Application instance. If the singleton has already been initialized with an instance, it disallows the second re-initialization (throw an exception? return error code? silently ignore?).Tin your singleton.Applicationobject, and that object is a sub-object of a class derived fromApplication. Therefore, there can be only one class derived fromApplicationthat gets instantiated when the program runs, right? Do you know at compile time which derived class this is?OtherSampleApplicationclass, you can have things like:SampleApplication& application = SampleApplication::GetInstance();OtherSampleApplication& otherApplication = OtherSampleApplication::GetInstance();