template <class T> class singleton
{
static T *ms_singleton;
public:
void create();
void destroy();
T *getPtr();
};
advanced c++ memory managment/singleton conflicts
I am using a memory managed base object, mmobject, which uses a ref-counted retain/pool auto-release system, with garbage collection at every entrance to the run-loop.
The mmobject is wrapped with a smart pointer, which calls retain/release as necessary.
This system has been stress-tested extensively, and works perfectly.
The problem comes when trying to inherit a class from both mmobject, and my templated singleton class, which is as follows.
when you take and then let drop a smart pointer of a singleton instance, depending on the code, either the garbage collector deletes the singleton, or else the singleton is added but never removed from the list of live objects.
The essence of the problem is that there is no way for the memory management system to tell if a given object is a singleton or not.
I have considered and tried several solutions to this problem, but none have produced the desired results:
1) placing an identically named function in each class to return whether or not it is a singleton, but the C++ runtime tends to crash over this, I guess the runtime does not support this.
2) inheriting singleton from mmobject, and overloading mmobject methods to prevent the singleton from being added to the memory pool, or deriving both from a separate base class with a flag to indicate singletoness, but the diamond-shape inheritance of this theory defeats itself as the constructors seem to be called in an inconsistent order, and it is hit and miss whether singleton or mmobjects implementation will be called.
I am at a complete loss now, and it is proving a real hindrance in the design of my engine (not being able to derive singletons from classes inheriting from mmobject), and I would appreciate any help,
SwiftCoder
Quote:Original post by thedevdanIf you allocate anything in their constructor (or create or whatever), you must deallocate the data as well (it will NOT be done automatigically for all intents and purposes).
You don't need singletons to be managed. They will be deleted when the program ends.
swiftcoder: You can add the function to all objects fine, you just need to make it virtual, but that sounds like a rather hackish solution.
Personally, I suggest making singletons have a seperate manager, since they don't need the same kind of management that normal objects will have (just delete any data they allocate after everything else has been cleaned up)
Seems I did not make my self clear:
Lets say I have a class 'KernelTask' that inherits from mmobject, and I have a class 'InputTask' that inherits from 'KernelTask', but it is also a singleton, so it also inherits from 'singleton'.
Now you have conflicting memory management systems, the singleton expects the programmer to create/delete it ant the start/end of the program, but the mmobject runs garbage collection on all derived objects, and so deletes the singleton every time through the run loop.
As you can see, this only happens with multiple inheritance, but it is very usefull to have singletons that the rest of the engine (such as the Kernel) can regard as memory managed objects, so pointerscan be stored in arrays (i.e. a std::list of tasks, including singleton tasks).
There was some, not very detailed discussion of this issue on the enginuity thread, but they ended up declaring all functions & variables of the Tasks as static, which is an inelegant solution at best.
Extrarius: Declaring the functions as virtual didn't work, as the runtime alternated between calling mmobjects implementation and singletons implementation, seemingly at random, not sure why this is, but I assume that I have not correctly defined the order of inheritance, or something.
Thanks for the input,
SwiftCoder
Lets say I have a class 'KernelTask' that inherits from mmobject, and I have a class 'InputTask' that inherits from 'KernelTask', but it is also a singleton, so it also inherits from 'singleton'.
Now you have conflicting memory management systems, the singleton expects the programmer to create/delete it ant the start/end of the program, but the mmobject runs garbage collection on all derived objects, and so deletes the singleton every time through the run loop.
As you can see, this only happens with multiple inheritance, but it is very usefull to have singletons that the rest of the engine (such as the Kernel) can regard as memory managed objects, so pointerscan be stored in arrays (i.e. a std::list of tasks, including singleton tasks).
There was some, not very detailed discussion of this issue on the enginuity thread, but they ended up declaring all functions & variables of the Tasks as static, which is an inelegant solution at best.
Extrarius: Declaring the functions as virtual didn't work, as the runtime alternated between calling mmobjects implementation and singletons implementation, seemingly at random, not sure why this is, but I assume that I have not correctly defined the order of inheritance, or something.
Thanks for the input,
SwiftCoder
Well, in the case you described, the easiest solution would be to keep the reference count up. Depending on how your system works, you could probably just have the managed singleton store an extra pointer to itself (raising the count) that it sets to 0(or whatever reduces the reference count) in the destroy method. Then you'd have to make sure you destroy all singletons before the final garbage collection.
In fact, if your managed object provides addref and release methods then you could just use those in create/destroy to keep the count up.
In fact, if your managed object provides addref and release methods then you could just use those in create/destroy to keep the count up.
I think that it doesn't make much sense for a singleton to be in a memory pool either.
For a singleton there is only mean't to be one instance so it should manage its own life-time, doesn't make much sense for it to be any kind of memory pool.
Another way you could do it instead of making all your type's inherit from mmobject to be in a pooling scheme, take it out of the type hierarchy, have some kind of resource manager or some kind of flyweigt scheme handle the creation of instances that return smart pointers to it or something.
For a singleton there is only mean't to be one instance so it should manage its own life-time, doesn't make much sense for it to be any kind of memory pool.
Another way you could do it instead of making all your type's inherit from mmobject to be in a pooling scheme, take it out of the type hierarchy, have some kind of resource manager or some kind of flyweigt scheme handle the creation of instances that return smart pointers to it or something.
A singleton is meant to have a global lifetime by virtue of its global accessibility. It makes no sense for it to be a memory managed object.
I really don't see why you are making your tasks be singletons. You don't need a global lifetime for them and you don't need them to be globally accessible. This reminds me of someone else's post actually from a few months back who was doing the same exact thing.
I really don't see why you are making your tasks be singletons. You don't need a global lifetime for them and you don't need them to be globally accessible. This reminds me of someone else's post actually from a few months back who was doing the same exact thing.
Quote:Original post by antareus
A singleton is meant to have a global lifetime by virtue of its global accessibility.
A singleton is designed to have global accessibility, but you do not necessarily need, or even want, all singletons to exist at all times, especially as some may be mutually exclusive.
Quote:Original post by antareus
It makes no sense for it to be a memory managed object.
I could not agree more.
Quote:Original post by antareus
I really don't see why you are making your tasks be singletons. You don't need a global lifetime for them and you don't need them to be globally accessible.
More to the point, why am I making my singletons be tasks? Take input for instance: Input is a singleton that every object can query about the state of the keyboard, mouse, joysticks, etc., but it needs to be a task so that it can update its state every time through the run loop.
Quote:Original post by Extrarius
Well, in the case you described, the easiest solution would be to keep the reference count up.
In fact, if your managed object provides addref and release methods then you could just use those in create/destroy to keep the count up.
Agreed, however, the singleton class is not supposed to have any idea of the memory management systems existence, and is primarily intended for non-memory managed classes. In addition, I can not derive singleton from mmobject without creating diamond shape inheritance in the cases I am trying to fix, and I am trying to find a more elegant solution than pure virtual classes.
Keep the good advice flowing...
SwiftCoder
[Edited by - swiftcoder on September 18, 2004 8:34:19 PM]
I tend to create my singleton template class with a static internal variable instead of a pointer. Automatic ctor/dtor calling. You could use placement new/delete to reinitialize it as well if you so desired:
#include <new>template < typename T > class singleton_handle{ static T data;public: T & operator* ( void ) { return data; } T * operator->( void ) { return &data } void remake( void ) { data.~T(); new (&data) T(); } template < typename Arg1T > void remake( const Arg1T & Arg1 ) { data.~T(); new (&data) T( Arg1 ); } template < typename Arg1T , typename Arg2T > void remake( const Arg2T & Arg2 ) { data.~T(); new (&data) T( Arg1 , Arg2 ); }};template < typename T > T singleton_handle<T>::data;useage:class CMySingleton { ... };typedef singleton_handle< CMySingleton > MySingleton;//alternative: singleton_handle< CMySingleton > mySingleton;//removes the need to create a MySingleton instance every place you use it.void whatever ( void ){ MySingleton mySingleton; mySingleton->CallFunction();}void pie ( void ){ MySingleton mySingleton; mySingleton->CallOtherFunction;}//if CMySingleton was a logger and you wanted to refresh it to change buffers:void switch_to_buffer( const std::ostream & os ){ MySingleton mySingleton; mySingleton.remake( os ); //calls ~CMySingleton() then CMySingleton( os )}
MaulingMonkey: Yes, storing the singleton variable as by value would prevent the memory manager from picking it up (memory manager ignores all objects on the stack), and this is, so far, the closest to what I want to do, so I may end up using it.
However, if I have read this correctly, because the data variable is static, it will never be deleted (unless you set data to 0 in the destructor of singleton_handle), and it also means the only way to delete the singleton is to let the singleton_handle go out of scope, which means we are back to scoping variables, which is exactly what the singleton and memory manager are trying to automate in the first case.
Apologies if I am being harsh on people's programming practices and software engineering, but I really would like to construct an as near to fool-proof design idiom for my game engine (and potentially everyone's future projects) as is possible, and having originally learned to program using higher-level languages with nearly-perfect memory management, I am sure there must be an answer that does not use incorrect, slow, or downright bad programming methods such as diamond-inheritance, or rtti.
Sorry for running on...
SwiftCoder
However, if I have read this correctly, because the data variable is static, it will never be deleted (unless you set data to 0 in the destructor of singleton_handle), and it also means the only way to delete the singleton is to let the singleton_handle go out of scope, which means we are back to scoping variables, which is exactly what the singleton and memory manager are trying to automate in the first case.
Apologies if I am being harsh on people's programming practices and software engineering, but I really would like to construct an as near to fool-proof design idiom for my game engine (and potentially everyone's future projects) as is possible, and having originally learned to program using higher-level languages with nearly-perfect memory management, I am sure there must be an answer that does not use incorrect, slow, or downright bad programming methods such as diamond-inheritance, or rtti.
Sorry for running on...
SwiftCoder
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement