
Jason Hise wrote:
I realize now that there is little interest in considering my library for Boost over an adaptation of the Loki library... however, even if my implementation is not under consideration, would someone be willing to explain what advantages Loki offers over it?
The key thing that Loki does is use modern C++ design methods to allow a policy driven choice of a number of design questions. These are: 1) The Creation Policy - this can mean creation from new, malloc, or create statically. 2) The Lifetime Policy - this can mean that you use a Phoenix Singleton, Longevity managed singleton, or just using normal C++ rules. 3) The threading policy - single or multi-threaded. The key disadvantage of your implementation (in my view) is that it forces the use of a single creation policy (create statically), a single lifetime policy (dependencies), and a single threading policy (single-threaded). Whilst it would be relatively easy to change any of these, it would involve re-writing the class, which moves away from the concept of generic, re-usable code.
Dave Handley wrote:
1) Wouldn't it be better to use CRTP than the rather strange usage syntax that Loki uses with a typedef and a requirement to make the constructors/destructors private? It is remarkably easy if you are not familiar with the Singleton implementation in Loki to make your constructors/destructors public by mistake and then use them, whereas a CRTP solution could make the base constructors/destructors protected.
With my implementation it is perfectly safe for client code to make public constructors and destructors and not have to worry about the class being used the wrong way, because the inheritance method I used makes it pure virtual.
I agree - and that is why I have stated the usage syntax in Loki is something I don't like - I would much prefer to see the curiously recurring template pattern (CRTP) being used. For your info, if you are not familiar with CRTP then you would have a new singleton called MySingleton that looked something like: class MySingleton : public generic_singleton<MySingleton> etc. The generic_singleton class can then enforce protected constructors/destructors and private copy constructors/equals operators. This has a few advantages over the model used in your code. The main thing is that it is a little more readable. Your version: Singleton<MySingleton>::GetInst() gets the singleton pointer whereas with CRTP: MySingleton::GetInst() would get the singleton pointer.
2) There are probably some more options that need to be included, for example making a Singleton be capable of destroying/recreating during the run of a program. Consider a singleton that is managing a print queue. For some reason all the printers go off-line, there is a potential for memory optimisation by closing down the print queue. If printers come back on line the print queue could come back on line - but that isn't possible (as far as I know) with Loki.
With my revised usage of dependencies, the singleton would be destroyed when there were no dependencies left and recreated when new ones were added. If client code wants to ensure that the singleton's life is not interrupted, they could add a dependency in main, or even add a static/global dependency to ensure that the lifetime extends beyond main if that feature is desired. Using dependencies also can ensure that singletons which rely on each other are always created and destroyed in the right order.
Dependencies are certainly one way of managing the lifetime of a singleton, but in a large project you would need to be aware of some potential flaws which your implementation would not pick up. Specifically, if you accidentally created a cycle of dependencies, the Singletons will never be destroyed. This is an easy thing to pick up in a small project, but in a large project maintained with lots of programmers over a number of years, I can guarantee that this will happen at some point. This is one of the reasons for a longevity based system in Loki - it doesn't matter if you get 2 singletons with the same longevity since they will just be deleted in reverse order of creation.
Again, I am unfamiliar with Loki, so I am sure that there are considerations that it handles of which I am unaware. I would just like to learn what these considerations are so that I might grow as a programmer, and learn through improving my own implementation. Thanks in advance.
A final point which I'm pretty sure someone mentioned on a previous post. Your implementation includes a virtual function in the SingletonBase. A singleton is a simple object that doesn't necessarily need a v-table. By having a virtual function in the base, you can never create a singleton that doesn't have a v-table. Also as an aside, whilst both SingletonBase and Singleton contain virtual functions, they do not declare the destructor virtual but probably should! Using CRTP, you should get rid of the need for any virtual functions. Remember that Singletons should be pretty simple - look at the code in the GoF book and you see what I mean: // Declaration class Singleton { public: static Singleton* Instance(); protected: Singleton(); private: static Singleton* _instance; } Your solution isn't wrong, its just that Loki does a whole lot more that makes it a better library. The point I am making with my posts is that if Boost is going to create a library with a singleton in it, it should, as a minimum, do everything that Loki does. It should also try to fix some of the problems with Loki as well along the way... Dave

Dave, thank you for your reply. Dave Handley wrote:
The key disadvantage of your implementation (in my view) is that it forces the use of a single creation policy (create statically)
Since my revision, the singleton is not forced to be created statically. Singleton A will only be created statically if client code creates a static dependency on A, or if another singleton B has a static lifetime and depends upon A. If the actual method of allocation (new) should be made more flexible, would adding a template allocator parameter similar to that used by stl containers be sufficient?
...a single lifetime policy (dependencies)
Again, because I removed the enforced static dependency that was in the original code, my singleton should allow multiple lifetime types based on where and how dependencies are used.
and a single threading policy (single-threaded).
This is accurate, and I do not know how it could be portably resolved. Hopefully when Alexandrescu's book gets here I will get some insight on how this was resolved in Loki.
I would much prefer to see the curiously recurring template pattern (CRTP) being used. For your info, if you are not familiar with CRTP then you would have a new singleton called MySingleton that looked something like:
class MySingleton : public generic_singleton<MySingleton> etc.
The generic_singleton class can then enforce protected constructors/destructors and private copy constructors/equals operators.
I was not aware that making a base class's ctors and dtors protected would prevent client code from instantiating a derived class that chooses to make its ctors and dtors public. Now aware of this, I fully agree that such an organizational structure would be preferable to my own.
Dependencies are certainly one way of managing the lifetime of a singleton, but in a large project you would need to be aware of some potential flaws which your implementation would not pick up. Specifically, if you accidentally created a cycle of dependencies, the Singletons will never be destroyed. This is an easy thing to pick up in a small project, but in a large project maintained with lots of programmers over a number of years, I can guarantee that this will happen at some point.
Doesn't the same potential problem exist with ref-counted smart pointers? Wouldn't it be accurate to say that it should be illegal to create singletons with cyclic dependencies in the first place? With my current design, just getting a pointer to the singleton introduces a dependency. Thus, if a singleton B requires access to a singleton A for only certain function calls, it should simply get access to the pointer to A's instance at the beginning of those function calls. A singleton should only own a dependency to another singleton if it actually requires it for its entire lifetime, so cyclic dependencies simply don't make sense to write.
This is one of the reasons for a longevity based system in Loki - it doesn't matter if you get 2 singletons with the same longevity since they will just be deleted in reverse order of creation.
Hard coding a longevity number for each singleton seems dangerous to me, because it is difficult to see what order all the singletons would be created in without tracking each down and looking at it. Any time a new singleton is added, many of the numbers might need to be shuffled. Explicitly owning a dependency or getting the instance seems more intuitive to me as to document what the singleton actually requires.
Your solution isn't wrong, its just that Loki does a whole lot more that makes it a better library.
Hopefully, after rearranging my code into CRTP form and adding a template parameter to control allocation and deallocation, the only real drawback to my library will be its lack of support for different threading models. After reading up on how Loki achieved this, I will try to integrate it as well. Again, thank you for your feedback. -Jason

----- Original Message ----- From: "Dave Handley" <dave@dah.me.uk> To: <boost@lists.boost.org> Sent: Saturday, January 08, 2005 9:39 AM Subject: [boost] Re Singleton
I realize now that there is little interest in considering my library for Boost over an adaptation of the Loki library... however, even if my implementation is not under consideration, would someone be willing to explain what advantages Loki offers over it?
The key thing that Loki does is use modern C++ design methods to allow a policy driven choice of a number of design questions. These are:
1) The Creation Policy - this can mean creation from new, malloc, or create statically. 2) The Lifetime Policy - this can mean that you use a Phoenix Singleton, Longevity managed singleton, or just using normal C++ rules. 3) The threading policy - single or multi-threaded.
The key disadvantage of your implementation (in my view) is that it forces the use of a single creation policy (create statically), a single lifetime policy (dependencies), and a single threading policy (single-threaded). Whilst it would be relatively easy to change any of these, it would involve re-writing the class, which moves away from the concept of generic, re-usable code.
While this is a response to a somewhat out-of-date message, I have read the latest contributions. This is just the most relevant to what I am about to add. Why does the "singleton" concept become a type that only ever has the one instance? I know this sounds ridiculous but I have stumbled onto this issue a few times and it continues to irritate me. One of the approaches to singletons goes something like; struct very_important_object {}; very_important_object & single_instance() { static very_important_object *p = new very_important_object; return *p; } This is not presented as the One-True-Singleton-Strategy, but simply as a technique that doesnt place the enforcement of singleton in the type. Why would you ever have more than one singleton? <embarrassed grin> Imagine that you are writing a PBX. There are several major components to this (exactly which depends on the standard you are following) and sensibly these components are to be implemented as singletons. Imagine that the type-equals-only-instance approach was adopted. Further imagine that the implementation all goes swimmingly. Then one day you are asked to produce a self-verifying version of your switch that runs two instances side-by-side. Or you are asked to do some call-setup performance testing, i.e. you need to run two instances of your PBX against each other. Or you need to perform soak testing that needs 6 instances in a single executable. And so on. Should the abstraction known as Singleton truly preclude there ever being more than 1 instance? Hopefully this list of possibilities has enough substance to it. And captures what I am trying to say ;-) Otherwise I am going to feel quite silly. Sometimes the type-equals-only-instance is a royal pain? Cheers, Scott

Scott Woods wrote:
<>...one day you are asked to produce a self-verifying version of your switch that runs two instances side-by-side. Or you are asked to do some call-setup performance testing, i.e. you need to run two instances of your PBX against each other. Or you need to perform soak testing that needs 6 instances in a single executable. And so on. Should the abstraction known as Singleton truly preclude there ever being more than 1 instance?
This is an interesting point. Perhaps the derived template class could be templated itself to take one parameter, an int. Then a new instance of the singleton could be referenced at any time by simply referring to the singleton type with a different unique ID for the template parameter. The field could default to zero for normal usage as a singleton. Ex: template < int I = 0 > class Important : Singleton < Important > { protected: Important ( ) { } ~ Important ( ) { } public: // important stuff here }; void TestWithThreeInstances ( ) { Important < 0 > :: Pointer instZero = Important < 0 > :: GetInst ( ); Important < 1 > :: Pointer instOne = Important < 1 > :: GetInst ( ); Important < 2 > :: Pointer instTwo = Important < 2 > :: GetInst ( ); // do testing here } -Jason

Jason Hise wrote:
template < int I = 0 > class Important : Singleton < Important > { ... };
Just a quick self correction, I meant: template < int I = 0 > class Important : Singleton < Important < I > > { ... }; Having all specializations derive from a Singleton < Important < 0 > > would have been a bad thing.
participants (3)
-
Dave Handley
-
Jason Hise
-
Scott Woods