
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

"Dave Handley" wrote:
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. I would like to point this could be real problem: it the code resides in a DLL whose new/delete are overloaded, static initialization (and singletons constructed during this time) may cause nasty and hard to debug crashes later.
Having Allocator/malloc/etc policy is necessary there. /Pavel

Dave Handley wrote:
Providing an allocator template is essentially what is done in Loki so that would suffice to resolve this particular difference.
I have recently converted my code to CRTP form and noticed an inherent problem with creating an allocator template now. Because the derived class is the final singleton class, and its ctor and dtor are protected, there is no way to give a generic allocator the right to create the object. Even attempting to write a secret friendly shortcut through the base can't work, because the base doesn't have access to its children's protected members. I'm trying to use the template specialization method that Herb Sutter mentions in Exceptional C++ Style #15, putting protected templated functions in the base and then specializing the child versions to get access to the ctor. Unfortunately, all my attempts have failed. Here is a stripped down example of what I am trying (destruction would be virtually identical, and thus is omited): template < typename T > class AllocateUsingNew { public: static T * Create ( ) { T * p = reinterpret_cast < T * > ( new char [ sizeof ( T ) ] ); try { return T :: Allocation :: Construct ( p ); } catch ( ... ) { delete [] reinterpret_cast < char * > ( p ); throw; } } }; template < typename T, typename A = AllocateUsingNew < T > > class Singleton { public: class Allocation { private: friend A; // allow the allocator access to Allocation::Create static void Create ( T * p ) { T :: Create < T > ( p ); // call the derived specialization? } }; friend Allocation; // allow allocation access to Create protected: template < typename T > static void Create ( T * p ); // undefined base template that derived should inherit }; template < typename T > void T :: Create < T > ( T * p ) // specialize derived's Create method? { new ( p ) T; } Any suggestions?
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.
Or... you could just add a static dependency to that specific singleton in the code where the singleton is defined ;)
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).
Alright, I can accept that there may be different lifetime models that are needed. The phoenix singleton, however, is already handled by my current model, which attaches a dependency to the smart singleton pointer when GetInst is called. Unfortunately, I have been looking at the Loki singleton code (hopefully the book will get here soon), and am having trouble seeing how a dependency model could be implemented with Loki's lifetime traits model. Could you give me an example of how this could be achieved? -Jason

I have recently converted my code to CRTP form and noticed an inherent problem with creating an allocator template now. Because the derived class is the final singleton class, and its ctor and dtor are protected, there is no way to give a generic allocator the right to create the object. Allocator just allocates N bytes, object creates itself via
"Jason Hise" wrote: placement new. static T * Create ( ) { void * p = Allocator::allocate(sizeof(T)); try { return new (p) T(...); } catch ( ... ) { Allocator::deallocate(p); throw; } } Instead of try/catch shared_array should be used to avoid destroying stack frame when exception throws. /Pavel

Pavel Vozenilek wrote:
Allocator just allocates N bytes, object creates itself via placement new.
This will not work unless every class that derives from singleton defines its own create method. The singleton base itself cannot call placement new, because it does not have access to the protected parts of a derived class.
Instead of try/catch shared_array should be used to avoid destroying stack frame when exception throws.
I'm tempted, except for the fact that it would only be needed in this one place and adds a dependency on the shared_array library, which is only needed for an implementation detail. Right now, there are no external dependencies and I don't think it would be worth it to save my implementation three lines of code. -Jason

"Jason Hise" wrote:
Allocator just allocates N bytes, object creates itself via placement new.
This will not work unless every class that derives from singleton defines its own create method. The singleton base itself cannot call placement new, because it does not have access to the protected parts of a derived class.
I think there are posibilities: 1. constructor/destructor of class T is public (the class may be instantiated for other purposes as well) 2. the class provides static member function for construct/destroy or there's object factory for it. Singleton may provide specializations: - default one using placement new/delete (+ allocator) template < typename T, typename A = AllocateUsingNew > /* template < typename T, AllocateUsingMalloc> template < typename T, CreateStatic> */ - one where you provide class member functions as template parameters template < typename T, CreateViaFunctors<T*(* Creator)(params), void(* Deleter)(const T*)> > - something like above for object factories
Instead of try/catch shared_array should be used to avoid destroying stack frame when exception throws.
I'm tempted, except for the fact that it would only be needed in this one place and adds a dependency on the shared_array library, which is only needed for an implementation detail. Right now, there are no external dependencies and I don't think it would be worth it to save my implementation three lines of code.
It is possible to create tiny RAII class(es) just for this purpose. /Pavel

I thought of a solution to access the derived class's ctor which by all means should work, however I'm probably missing something obvious in this test version of my idea. Why does my compiler say that SingletonAllocationHelper < D > doesn't have access to D's ctor? template < typename T > class Singleton { public: static void foo ( ) { Create ( ( T * ) 0 ); } protected: Singleton ( ) { } private: static void Create ( T * p ) { SingletonAllocationHelper < T > :: Create ( p ); } }; template < typename T > class SingletonAllocationHelper : public T { private: friend class Singleton < T >; SingletonAllocationHelper ( ); SingletonAllocationHelper ( const SingletonAllocationHelper & ); ~ SingletonAllocationHelper ( ); static void Create ( T * p ) { new ( p ) T ( ); } }; class D : public Singleton < D > { protected: D ( ) { } }; int main ( ) { D :: foo ( ); } -Jason

"Jason Hise" wrote:
Why does my compiler say that SingletonAllocationHelper < D > doesn't have access to D's ctor?
[snip] To use placement new it is needed to have overloaded new/delete operators (or overloaded globals). With the definition bellow the code compiles on Intel: class D : public Singleton < D > { protected: D ( ) { } void* operator new(size_t, D* p) { return p; } void operator delete(void* p, D*) {} }; /Pavel

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Jason Hise Sent: Saturday, January 08, 2005 11:56 AM To: boost@lists.boost.org Subject: Re: [boost] Re: Singleton
Here is a stripped down example of what I am trying (destruction would be virtually identical, and thus is omited):
template < typename T > class AllocateUsingNew { public: static T * Create ( ) { T * p = reinterpret_cast < T * > ( new char [ sizeof ( T ) ] ); try { return T :: Allocation :: Construct ( p ); } catch ( ... ) { delete [] reinterpret_cast < char * > ( p ); throw; } } };
template < typename T, typename A = AllocateUsingNew < T > > class Singleton { public: class Allocation { private: friend A; // allow the allocator access to Allocation::Create
static void Create ( T * p ) { T :: Create < T > ( p ); // call the derived specialization? } };
friend Allocation; // allow allocation access to Create
protected: template < typename T > static void Create ( T * p ); // undefined base template that derived should inherit };
template < typename T > void T :: Create < T > ( T * p ) // specialize derived's Create method? { new ( p ) T; }
Any suggestions?
Your Create() function in AllocateUsingNew isn't guaranteed to work for all classes. The type of the singleton class might have alignment restrictions. char has no alignment restrictions; therefore, the program may crash when trying to initialize the singleton. I believe boost::aligned_storage is your friend in this situation. -- Noah

Noah Stein wrote:
Your Create() function in AllocateUsingNew isn't guaranteed to work for all classes. The type of the singleton class might have alignment restrictions. char has no alignment restrictions; therefore, the program may crash when trying to initialize the singleton. I believe boost::aligned_storage is your friend in this situation.
Thanks for the heads up. Although the implementation of AllocateUsingNew has since been changed and now is able to use regular new, my static allocation class still would have this problem. I'll look into aligned storage immediately. -Jason

Noah Stein wrote:
Your Create() function in AllocateUsingNew isn't guaranteed to work for all classes. The type of the singleton class might have alignment restrictions. char has no alignment restrictions; therefore, the program may crash when trying to initialize the singleton. I believe boost::aligned_storage is your friend in this situation.
I looked into aligned storage, and discovered that it is interwoven deep into boost, and includes many dependencies. Although I think it would be great if this library became a part of boost, I don't want to make it dependent upon becoming a part of boost just to solve the aligned storage issue. Perhaps I could add extension headers, which would contain the extra policies that require specific platforms or libraries. Then my singleton header would only include them if a corresponding tag (BOOST_AVAILABLE) has been defined or something of that nature. Of course, such action would be a last resort... if aligned_storage is a simple enough device, I would prefer to just inline it as an implementation detail of the static allocation policy. How dependent upon the compiler being used is the implementation of aligned_storage? Is implementing a bare-bones version myself feasible? -Jason

Noah Stein <noah <at> acm.org> writes:
On Behalf Of Jason Hise T * p = reinterpret_cast < T * > ( new char [ sizeof ( T ) ] );
Your Create() function in AllocateUsingNew isn't guaranteed to work for all classes. The type of the singleton class might have alignment restrictions. char has no alignment restrictions; therefore, the program may crash when trying to initialize the singleton. I believe boost::aligned_storage is your friend in this situation.
Are you sure there is a problem here? Doesn't 5.3.4/10 allow this. The explanatory note reads in part: "...this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed."

Darryl Green wrote:
Noah Stein <noah <at> acm.org> writes:
n Behalf Of Jason Hise T * p = reinterpret_cast < T * > ( new char [ sizeof ( T ) ] );
Your Create() function in AllocateUsingNew isn't guaranteed to work for all classes. The type of the singleton class might have alignment restrictions. char has no alignment restrictions; therefore, the program may crash when trying to initialize the singleton. I believe boost::aligned_storage is your friend in this situation.
Are you sure there is a problem here? Doesn't 5.3.4/10 allow this. The explanatory note reads in part:
"...this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed."
I have a question about a potential solution. Say I were to define a union that contained a T, and then had a static instance of that union lying around as the memory location to use for storage (using placement new and explicit destruction to create and destroy the instance). Does this result in undefined behavior (due to T not being a C-style struct), or would it be legal C++? -Jason
participants (5)
-
Darryl Green
-
Dave Handley
-
Jason Hise
-
Noah Stein
-
Pavel Vozenilek