advanced c++ memory managment/singleton conflicts

Started by
16 comments, last by daerid 19 years, 7 months ago
Quote:Original post by 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.


You have misunderstood some.

Because the data variable is static, you can't call delete on it. You can, however, explicitly call it's destructor (which exists for all classes), and do a placement new (calling it's constructor if it has one). The area of memory is pre-allocated and is freed up at program termination, just like any global variable (or any variable on any modern OS). It's constructor does get called* (on GCC I believe it's at the first time the static variable is ever used) and it's destructor will get called* when the program exits.

* by "will get called" I mean "if things don't seriously fubar in the program, and your compiler isn't having problems combining internal initilization/cleanup code accross DLL and EXE boundries."

singleton_handle goes in and out of scope - because it's static it's shared between all the handles.

The one problem I'm aware of with this method is the order of initialization and deinitialization - if one singleton depends on another things can get messy real fast (when one gets uninitialized before the other, usually). To explicitly (de)initialize the array, you'd need to change the definitions to something like this:

#include <new>class singleton_handle< typename T >{    static char[ sizeof(T) ] c_data;public:    //note: init nor fini check if the handle has been initialized or not yet.    void init ( void ) { new ((T *)c_data) T(); }    template < typename Arg1T > init( const Arg1T & Arg1 ) { new ((T *)c_data) T( Arg1 ); }    void fini ( void ) { ((T *)c_data)->~T(); }    //note: this will fubar if things havn't been initialized properly.    T & operator*( void ) { return *((T *)c_data); } //prevents casting to (const) temp var.    T * operator->( void ) { return ((T *)c_data); }};template < typename T > char singleton_handle<T>::c_data[ sizeof(T) ];


edit: deleted ampersands (&) that didn't belong.
Advertisement
Here's my favorite singleton, I've found there really isn't many scenarios I've found where it doesn't fit:

template<class T>class singleton{public:  singleton()  {    assert(m_ptr == 0);    m_ptr = static_cast<T>(this);  }  ~singleton()  {    assert(m_ptr != 0);    m_ptr = 0;  }  T& get_instance()  {    assert(m_ptr != 0);    return *m_ptr;  }private:  T* m_ptr;};template<class T>T* singleton<T>::m_ptr = 0;


To use, you just inherit from singleton<YourClass>.

This way, you can manage the lifetime of the singleton exactly as you would an automatic variable. Just make instances of your singleton classes right inside your main() function, and those variables can now be accessed by every part of the program. This also allows you to control the order of initialization and destruction.

This way, your singleton class wouldn't even have to know your mem mgr class exists.
daerid@gmail.com
Quote:Original post by swiftcoder
[...]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 [...]


Make your memory manager to delete singletons at the end of memory manager's life time.
[size="2"]I like the Walrus best.
Quote:Original post by daerid
Here's my favorite singleton, I've found there really isn't many scenarios I've found where it doesn't fit:
*** Source Snippet Removed ***
To use, you just inherit from singleton<YourClass>.

This is actually the same singleton I am using, I just added create and destroy as wrappers around new and delete, and to do some debug checking, and I only implement m_ptr in the source files I need to use it in:
// Task.hclass Task : public Singleton<Task>, public mmobject{...}// task.cppTask *Singleton<Task>::m_ptr = NULL;// which allows the singleton to be used without too many// problems in any flavour of dynamic library.

Quote:Original post by daerid
This way, you can manage the lifetime of the singleton exactly as you would an automatic variable. Just make instances of your singleton classes right inside your main() function, and those variables can now be accessed by every part of the program. This also allows you to control the order of initialization and destruction.

This way, your singleton class wouldn't even have to know your mem mgr class exists.

This is how I was trying at first, but because the singleton is created using new, the memory manager picks it up as dynamically allocated, and places it in the object pool, and when the smart pointers go out of scope, the singleton tends to get released. I am thinking of going back to this method, and just not worrying about it, i.e. just let the singleton go out of scope when all smart pointers have gone out of scope, since in most cases where a singleton is memory managed, it does not need global lifetime.

Now another question, in the above code snippet, where Task inherits from both singleton and mmobject, if both classes have an identically named virtual method, which classes version of the function will be called (i.e. if singleton and mmobject both had a method: virtual void release(); and you call Task->release(); which one will be called, assuming that Task has not overridden the release()?).

Thanks,

SwiftCoder

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

The singleton-ness is screwing you over here. Why not just pass parameters around as needed and let things like Input just remain a task? What I'm saying is that I don't see how it is desirable that *any* object can get access to the input.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
In your singleton, instead of doing:
m_ptr = new T;

try this:
m_ptr = ::new T;

and for delete:
::operator delete(m_ptr);

This will force the invocation of the global new operator (and I assume you just override a new/delete operator in mmobject). So, as long as the global operators aren't overridden (which is often a bad idea anyway), should be fine.
"Is life so dear, or peace so sweet, as to be purchased at the price of chains and slavery?" - Patrick Henry
Quote:Original post by Desert Fox
In your singleton, instead of doing:
m_ptr = new T;
try this:
m_ptr = ::new T;
and for delete:
::operator delete(m_ptr);


Perfect, This just what I was looking for, twenty minutes after i realised that my original system worked fine, and that I have been talking myself around in circles :)
Thanks anyway.

I had just realised that this whole thread was a waste of everybody's time, because of the very reason i posted it, namely that if the singleton also inherits from the memory managed object, it obviously wants to be garbage collected, and doesn't need to exist for the whole lifetime of the program.
i.e. in the case of the Input Task, as soon as the Kernel lets the reference to it go out of scope, it obviously is not needed any more, so we just let garbage collection delete it, instead of having to call delete manually (and it can always be created again if necessary).

I still can't believe how unbelievable stupid I was not to see this immediately, it was staring me right in the face.

Many thanks for all you input (which made me truly think about this),

SwiftCoder

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

That's great that you found the solution. Just to clear things up, the singleton I posted doesn't require you to instantiate it by using operator new. You allocate the object on the stack, usually directly in the main() function, and the singleton template allows you to access it in any function below it.
daerid@gmail.com

This topic is closed to new replies.

Advertisement