This namespace is the home of the very tiny and low-level ALib Module "Singleton". While the scope is almost too small to be named a module, for organizing the library as "orthogonal" as possible, the separation is helpful. And, should a programmer be really not interested in anything else of ALib than the singleton feature, this module is well extractable from the rest of the library and only a few files remain with no "clutter" around class Singleton.
It could be said, that this manual is written in reverse order. The use case of the library and its alternatives is only given in the last chapter. The reason for this is that presenting the solution first, just makes it more easy to understand when this library is applicable.
Consequently, experienced programmers might want to read sections 9.1 Use-Cases and 9.2 Alternatives first!
The only class found in this module is Singleton, which implements the well known Singleton Design Pattern. With C++ templates, the usually proposed solution is so simple, that it would not justify even this tiny module. Such simple solution looks as follows:
This works fine in general unless a software running on Windows OS, starts to access the same singleton object from within different Windows DLLs or a Windows DLL and the main executable. Although running in one process, the singletons retrieved are singletons "per DLL/Executable". The reason for this is simply spoken that Windows DLLs dispose of an own data segment for global variables.
This is different with shared libraries under GNU/Linux or macOS, which are designed to behave exactly as statically linked libraries.
The code provided with this repository creates true singleton objects, even when Windows OS DLLs are used. This is achieved by creating a static hash map (only once) and collecting all singletons herein. The C++ runtime type information struct std::type_info
is used as the key to the singletons in the hash map.
As said above, the effort done with this implementation is needed only if a software process uses different data segments for different portions of the code, for example processes which open DLLs on Windows OS. As this effort imposes a (small) overhead, the implementation of this class can be switched to a simple implementation version, which just stores the singletons in a static field of the class. The implementation is controlled by compiler symbol ALIB_FEAT_SINGLETON_MAPPED. On Windows OS the symbol defaults to true
, otherwise to false.
Changing these defaults could be done for the following reasons:
The implementation details described above are transparent in respect to using the class.
A user type may inherit the singleton feature from this class by being derived from it, with the template parameter TDerivedClass set to name just of the derived class type itself. Then, static method Singleton::GetSingleton will return a singleton of the derived type.
Here is a sample code deriving from this class:
Now, the singleton can be requested as follows:
Note that the object returned by method Singleton::GetSingleton is not automatically the only instance of the custom class: While it is "the singleton" object, other instances might still be created. Usually, the term "strictness" is used to denote if a singleton type is to be allowed to have other instances of the type next to the singleton instance or not.
In this respected, types derived from class Singleton are not strict, as shown here:
To create classes that implement strict singletons, two things have to be done:
This is shown in the following sample:
Before terminating the process, the singletons collected in the global map, can be destructed and deleted using namespace function Shutdown. See this method's description for more information.
Especially note that default module termination (namely invoking function Shutdown invokes this method already.
main()
. The reason for this is that the hashtable used to store singletons, is likewise a global object and might not be initialized at that point in time yet.As described above, to extend the singleton concept across DLL bounds, a static hash map is needed. Now, with debug-builds (see compiler symbol ALIB_DEBUG), this map can be accessed with namespace function DbgGetSingletons.
If the ALib Distribution includes module ALib Strings, then an overloaded version exists with DbgGetSingletons(NAString&). This function performs a simple dump of all singleton types into the given AString.
With the explanations given in the previous chapters, the precise use-case for this library can be defined:
In other words: The ultimate goal of this module was to allow a library to present a templated singleton type to a using code. When the using code created a custom instantiation of the type, no need for a parallel definition of a global singleton instance for this template instantiation should be needed.
This might be explained with a quick sample. Module ALib Boxing provides type Box that is a container for any sort of object. For example:
Box box= 42;
stores an integral value in a "box". The assignment operator (here constructor) stores a pointer to a singleton object along with the value. The singleton is of type VTable<T>, where T is the type that was boxed. In the case above VTable<int>. This singleton is used with any instance of class Box that holds an int value.
Now, if a custom code says:
MyType myObject; Box box= myObject;
a pointer to a singleton of type VTable<MyType> is needed. The use of class Singleton allows creating such singletons "on-the-fly" without the need of a corresponding global object definition.
This module should maybe not be used (to avoid unnecessary dependencies of your code to this library) in the following situations:
It is possible to "optimize" the creation of singletons, when using this module, by providing specializations of dedicated "foreseeable" instantiations of the custom template type that is derived from class Singleton.
As a sample, we stick to the one presented in section 9.1 Use-Cases. For all fundamental types and other important, aka "frequently boxed" types, the singleton mechanism of class Singleton is "disabled". Hence, if an integral is boxed:
Box box= 42;
then the singleton of the VTable object is not created by method Singleton::GetSingleton. Only for "unknown" types this is done.
With that, not only the code size of the templated constructor of class Box shrinks, but it is allowed to create global objects of type Box that are initialized with values of such types.
In contrast to what was proposed above, namely by giving specializations of the templated VTable type, module ALib Boxing takes a slightly modified approach: The VTable singletons are exclusively received through a next template type T_VTableFactory Instead of specializing the VTable, this type is specialized for "frequently used boxed types".
For further details on this implementation, consult chapter 12.2 Optimizations With Static VTables of the Programmer's Manual of module ALib Boxing and feel free to review relevant source code found in alib/boxing/detail/vtable.inl.