
On Jan 16, 2008 3:15 AM, Tobias Schwinger <tschwinger@isonews2.com> wrote:
thank you for your review.
You are most welcome. Thank you for your response to my comments.
For instance, the design could be made more flexible by allowing thread-safety to be specified as a policy (with appropriate default) or by delegating the responsibility to the wrapped type.
OK, FWIW policies complicate things. And what's their benefit here?
In some cases, policies do complicate things. For instance, while I have marveled at the Loki Singleton implementation, it is at one extreme of the general-purpose design spectrum and I'm not willing to pay the price (in complexity). I find your design is, on the other hand, at the special-purpose end of that spectrum, and I'm not sure that's necessary.
If BOOST_HAS_THREADS is defined, it looks as though I have no choice but to use mutexed instantiation and/or access. If I'm not interested in synchronised access, then the lease interface is useless.
Wrong:
The lease interface is useful to improves performance. The test whether the Singleton has been initialized (which requires synchronization) can be performed only once.
The test whether the Singleton has been initialized does not require synchronisation if you are not in an MT context or if the MT context is handled elsewhere in the design. The performance improvement is nil when compared to Meyer's singleton, which is the obvious choice in a single-thread context. What I'm asking for is the opportunity to select the simplest, most efficient implementation I can get away with while keeping the interface generic. I see another argument in favour of a "customizable" synchronisation mechanism: potential for deadlocks. If there are any circular dependencies between singletons then you need some kind of lock restriction mechanism to catch them. You may tell me that having singletons depend on each other this way is "bad design" and you would be right, but bad designs are part of life ;-) In practice, I find that as soon as you allow singletons to creep into a design, they start to depend on each other.
The solution to the OOI problem does not scale, and I would go as far as saying that it doesn't work in practice. In my experience, it's a constant maintenance nightmare to try and keep the various slot assignments in the correct order (I have to deal with a large number of singletons; yes that is a problem in and of itself - this library will do nothing to discourage it).
You must have misunderstood the docs. OOI is not done with slots. The slots are to prevent unnecessary resurrection during destruction.
Okay, I should have said "OOD" (order of destruction) instead. So reading the documentation: Singleton instances are normally destroyed in reverse order of their construction, that is for a group of Singletons with their integral DisposalSlot template arguments being equal, where the destruction of these groups is ordered by the ascending DisposalSlot values. We could raise the DisposalSlot value of global_log from the previous examples to make sure it outlives other singletons that use 0 (the default): (...) That, to me, sounds like the slot mecanism is for handling dependencies, in which case one must be careful in selecting which singletons fit in which slot.
The only scheme I've personally had any success with, so far, involves some form of reference counting, i.e. singletons that depend on other singletons do so explicitly (by incrementing the other's refcount on construction; the singletons are destroyed when their refcount goes to zero).
You might have gotten away with it in practice, but introducing this kind of coupling seems really bad design to me.
The coupling is there anyways (in the singleton's client code), but it is only implicit: that's the problem with global variables and singletons are no different in that respect. What I described only makes the coupling explicit in the singleton's interface. It's not as bad as it may sound: think shared_ptr with a forward declaration of the single-instanced type.
Another reason why the proposed OOI solution fails in practice is that other singletons are not a singleton's only dependency: client code ( e.g. objects that reference the singleton) are dependencies as well and may affect the order of destruction.
What is the "proposed OOI solution" you are talking about?
Yours, although it appears I had mislabeled it. I'll think up a concrete example and write back.
You just can't assume the singleton manager will be the last thing to go - there may be other globals.
It's the "we should remove pointers from C++ because one can dereference NULL" kind-of argumentation.
That's a little unfair, but I think I see what you mean. In that case, I think you need to improve the documentation to clearly express the limitations of the design, e.g. "singletons cannot have dependencies on globals or singletons not of this library, and vice-versa".
BTW I find the very idea of being able to "force" singleton destruction quite scary, but I haven't looked at the implementation details.
It's necessary, as long as C++ runs on top of ABIs that don't support code to be run automatically when a dynamic library is unloaded and I can assure you that running code in an unloaded, dynamic library is a lot scarier :-).
Admittedly, I haven't had to deal with that use case in a long time, so I probably shouldn't comment on it.
Finally, this may constitute a minority opinion but I like to consider global access and single instance as orthogonal concepts. One may want to ensure a resource is only instantiated once without necessarily providing global access to that resource.
So Singletons should be dynamic arrays in your opinion?
I'm not sure what you mean, and I haven't had coffee yet so perhaps that's why ;-). How do dynamic arrays help separate the concepts of preventing a second instantiation and global access to the first instance? Cheers, pj