Title: Singelton and Shared libiraries
Description: Problem we just ran across.
myork - May 7, 2004 12:58 PM (GMT)
A singelton is a 'Pattern' that only allows one instance of a class.
Lots of libraries out there provide a template to help you define a sigelton and they basically look like this:
| CODE |
template<class SING,class LOCK = mutexType> class Singelton { public: static SING& getInstance() { /* * There are verations on how the single instnace is declared, * Some people like to use a static member of the class rather * than a static member of a function, but the same problem * occuers. */ LOCK mutex; static SING theOnlyAllowedInstance;
return(theOnlyAllowedInstance); } };
|
Then when you want your class to be a sigelton you would do the following:
| CODE |
class MyClass { frend class Singelton<MyClass>; MyClass(); // Constructor is private so only the Sigelton can build it. public: }
|
Now when you want the only instance of your class you would use:
| CODE |
| MyClass& var = Singelton<MyClass>::getInstance(); |
Everything nice and easy so far.
The problem with the pattern as defined above that it is not sharable across multiple shared libraries (that's dll in the windows world). A brave (or foolish statment to make you might think. But bare with me.
1) To make the class Singelton usefull it has to be a template class. Otherwise you would be redefining the class for each singelton you need and the whole point of a library is that you have a single definition that can be re-used.
2) Because it is a template you need to define the getInstance() method inline (in the header file). If you defined it in a cpp file you would need to instanciate every instance of the template in the same file, which is not possable as this is a library and you don;t know what your users are going to make a singelton.
Thus when you create your shared library (dll) that uses getInstance() it will have its own copy of Singelton<MyClass>::getInstance(). If you then have multiple dll's that use a Singelton<MyClass> each dll will have its own copy of getInstance() and the executable will also get its own copy of the Singelton::getInstance().
So the answer to the problem?
Well, we are still having meetings on that. Some people are thinking this is a feature of the pattern not a bug of its implementation. I personally live in the camp that its a feature, but we also need to be able to define a cross executable singelton (or pureSingelton).
deep_soul_2000 - June 16, 2005 01:32 PM (GMT)
Hi All,
I think you can solve the problem by choosing the singleton to be the subject class and not a friend class, as follow:
| CODE |
template<class SING,class LOCK = mutexType> class Singelton { public: static Singleton<SING>& getInstance() { /* * There are verations on how the single instnace is declared, * Some people like to use a static member of the class rather * than a static member of a function, but the same problem * occuers. */ LOCK mutex; static Singleton<SING> theOnlyAllowedInstance;
return(theOnlyAllowedInstance); } private: // add a private default constructor + copy constructor .... etc... }; |
EDIT:
:codeblks:
myork - June 16, 2005 03:26 PM (GMT)
Please show us how that solves the problem.
Remember we are talking about the singelton being instanciated across multiple DLL's!
Neken - August 3, 2005 12:45 PM (GMT)
Personnaly, i simply do this to create a singleton (don't know if it's a good way to do it though) :
Oups, my bad, wrong coding.
dr voodoo - August 3, 2005 03:42 PM (GMT)
One way is to tell the compiler to look for an object in a specific module, meaning you use explicit template instantiation. An other way would be to create the singleton when linking or select a specific object there and ignore all others.
These would be the runtime equivalents of how linking works with static libraries. The first will always need explicit instantiation of some kind. The 2 latter will need a more sophisticated linker then the default dynamic library loader provided by Win and Unix.
This linker must itself be a pure singleton which has to be created with the first method. Further the linker must be able to identify 2 classes which are the same. Here you will have to give each class an ID. typeid will not work because the typeinfo objects are non copy able and you can not reference them because it's not sure if they are loaded all the time. Further it's not sure that typeid will work with dynamic libraries.
If you have the linker set up, then either every pure singleton classes in each module register themselves and the linker remembers the first of each ID. From code you can now use the ID and ask the linker for this pointer.
The other way is that the linker allocates a pure singleton on the heap when it is asked for a pointer. Here a number of factory classes must register themselves at link time for the linker to be able to do this.
I'd go for the last. Using something like the following could work:
| CODE |
tamplate<class Who> class Singleton { public: virtual ~Singleton(){ operator delete(this); //To avoid problems with different heaps } private: Singleton(); Singleton(const Singleton&);
static Singleton*Create(){ return new Who; }
static void RegisterClass(Linker*linker) { type void*(*Func)(); linker->register((Func)&Singleton::Create, /*class id*/); } };
|
Now you use 2 module local singleton for the linking. One to get a pointer to the linker and one to register the class. It's possible that the second can be instanciated by putting it in the global scope right after the class and declared static. As the linker will always just retain the first Create for each ID it doesn't matter if they are issued in each translation unit.
Incubator - August 4, 2005 08:37 AM (GMT)
honestly i still think the plain old method works fine
| CODE |
class Singleton { private: static Singleton* instance; static bool instanceFlag; Singleton(); public: static Singleton* getInstance(); ~Singleton(); };
bool Singleton::instanceFlag = false; Singleton* Singleton::instance = NULL; Singleton* Singleton::getInstance() { if(! instanceFlag) { single = new Singleton(); instanceFlag = true; return instance; } else { return instance; }
|
this is thegeneral and official way.
Never failed me and didnt give me a memleak either.
(why try to make things morecomplicated when its definitly not needed anywhay :P )
myork - August 4, 2005 12:48 PM (GMT)
You should read up on the subject:
Design Patterns The Bible on the subject.
More Effective C++ Easier to read but very thourgh.
Your design is a know flawed design (described in both the above books) as the destructor is never called (so it does leak memory). The memory leak is not the important point, the important point is the destructor.
I could point out other problems (but that would mean I would have to do some work [like reading the above books B)] ) but I would suggest you read the above books on the subject first.
PS. you dont need the 'instanceFlag' flag you can just check if the 'instance' member is NULL.
PPS. You forgot about the copy constructor and assignment operator (once instanciated it can be copied [which could be dangerous for a singelton]).
Incubator - August 4, 2005 12:57 PM (GMT)
| QUOTE (myork @ Aug 4 2005, 12:48 PM) |
PPS. You forgot about the copy constructor and assignment operator (once instanciated it can be copied [which could be dangerous for a singelton]).
|
so did you :P
your code inthe first post only had a getInstance() method.
and how come the destructor is never called?
If, at some point in my code I call a delete on the singleton's instance then it will execute the destructor (and its public)
unless I'm missing some point here you should clariify
myork - August 4, 2005 01:05 PM (GMT)
| QUOTE (Incubator @ Aug 4 2005, 07:57 AM) |
| so did you :P |
Ahhh, but I have an excuse. :P
My first post is for a template Singelton base (not an actual singelton).
| QUOTE (Incubator @ Aug 4 2005, 07:57 AM) |
your code inthe first post only had a getInstance() method. and how come the destructor is never called? |
The destructor for static member of functions is well defined. So the destructor will automitically get called as the application exits.
| QUOTE (Incubator @ Aug 4 2005, 07:57 AM) |
| If, at some point in my code I call a delete on the singleton's instance then it will execute the destructor (and its public) |
It would be nice for the singelton to automatically clean itself up. :D
Also this is dangerous. What happens if another part of your code uses the singelton after you have deleted it. Now they will be using a pointer to an object that has been destroyed. Though this problem is solved by adding a static method to delete the pointer and set it to NULL. You just have to hope that another part of the code does not re-instanciate it.
The problem is who destroys the singelton. Can you garantee that after destruction nobody will re-use it. Unfortunately because C++ does not define the order of instanciation of global objects across translation units this becomes a non trivial problem.
The use of a function static member variable solves a lot of problems (but not all of them). Check out the books I listed above. :dj:
Incubator - August 4, 2005 01:18 PM (GMT)
then what would be the proepr code for the destructor? and on some sites they even put both the ctor and dtor private
myork - August 4, 2005 01:20 PM (GMT)
| QUOTE (Incubator @ Aug 4 2005, 08:18 AM) |
| then what would be the proepr code for the destructor? |
That depends on what is in your singelton.
The point is that a well behaved program will call the destructor on all objects that have been constructed before terminating. This will garantee easier maintance of the code (as most people will assume the object will be destroyed correctly even if they can not initially see how).
| QUOTE (Incubator @ Aug 4 2005, 08:18 AM) |
| and on some sites they even put both the ctor and dtor private |
A private destroctor just stops you calling delete on an pointer (I think [I should check that statment]).
If you don't give them a pointer then making it private is not required.
Going offline. Have to go to work. :angry:
Incubator - August 4, 2005 01:35 PM (GMT)
I already am at work :P
(in the middle of Java Hibernate stuff :) )
dr voodoo - August 4, 2005 02:32 PM (GMT)
| QUOTE (myork @ Aug 4 2005, 01:20 PM) |
| QUOTE (Incubator @ Aug 4 2005, 08:18 AM) | | and on some sites they even put both the ctor and dtor private |
A private destroctor just stops you calling delete on an pointer (I think [I should check that statment]). |
It does that but is generaly used to stops people from allocating an object on the stack.
You should make the destructor private/protected if you want to assure that your object is at some point deleted using delete this although personally I perfer using an auto_ptr<typeof(*this)>auto_destroy(this); .