
Jason, I just want to clarify a couple of points in your last post: <snip>
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?
I've just looked at your code again, and I actually note that the only creation policy you use is effectively the CreateUsingNew policy of Loki. The singleton is always created using a new in the AddDependency function. Providing an allocator template is essentially what is done in Loki so that would suffice to resolve this particular difference.
...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.
Sorry, I think you have misunderstood me. I know that the dependencies method can be used in different ways, but it is still only a single policy. The reason for providing completely different policies for the lifetime policy is just that different projects require different solutions. I know for example that the dependencies model would fall down very quickly in the system I code in at present. We have a singleton that is the root of the tree that contains all the data in the system. Everyone uses this singleton, but doesn't tend to hold out references (dependencies). Under your dependency model, we would be almost forced to place a dependency in the main function - but here lies the problem. We actually have a number of different products which share the code base, but provide different main functions (and a few key interface classes) - this would mean we are coupling the main singleton to loads of places in the code. Using lifetimes, we can trivially make sure that this is the last singleton to come down.
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.
There is exactly the same problem with reference counted smart pointers. That is why the boost smart pointer library includes the weak_ptr. If you (for example) write a composite tree, and use smart pointers in both directions, the tree will never be destroyed - so you normally make the parent pointer a weak pointer. You are absolutely right in saying that it makes no sense to have a cyclic singleton dependency - but I can guarantee that in a big enough system, someone would make the mistake and put one in - especially since you don't explicitly check for cyclic dependencies (something that is hard to do anyway).
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.
Generally, if I use longevities, I place the longevity numbers in a header file. Either explicitly creating a longevity number for every singleton in there, or creating a set of longevity numbers that every singleton uses (possibly sharing numbers when there are no dependencies). I'm not saying dependencies are wrong - I'm just saying that a generic implementation should really provide the choice. I prefer to use longevities - you clearly prefer the dependencies - other people prefer to use Phoenix Singletons (singletons that re-create themselves from their own ashes if they are re-accessed after destruction). Even though I prefer longevities, there are some systems where I would probably use a different lifetime policy because it made sense to do so there. I would personally recommend having a read of Alexandrescu's book, especially the sections on the lifetime policies, then browsing the Loki code. If you implemented the allocation policy, used CRTP, implemented a threading policy, and also looked at integrating some different lifetime policies, then I personally think that you would have an ideal library to put forward for inclusion in Boost. Dave