shared_ptr singleton
Hello, I'm getting some troubles with shared_ptr when trying to implement a singleton pattern. Actually, it's not the singleton itself (which seems to work, in a separate program), but in the context I need it, doesn't work. I tried to figure out why (and actually I found an alternative implementation which works), but still I'm trying to understand what goes wrong. I'm trying to do this: having a hierarchy of classes where there is one Base class and a number of derived ones, each class is in a different cpp file. All these classes must be registered in a Register class, which is itself derived from Object. Register is the singleton, implemented like this: class Register; typedef shared_ptr<Register> RegisterP; class Register: public Object { public: Register(); void add(string name) { // Stuff with name } static RegisterP global() { if (!_global) _global = RegisterP(new Register); // * return _global; } private: static RegisterP _global; }; To register the various classes, does exist a special class, not in hierarchy, like this: class ClassRegisterer { public: ClassRegisterer::ClassRegisterer(string name) { Register::global()->register(name); // ** } } #define REGISTER(C) ClassRegisterer _registerClass_ ## C ( #C) In each file of the of the project, this macro is used when a class is created: Foo.cpp class Foo: public Object {...}; REGISTER(Foo); Bar.cpp class Bar: public Object {...}; REGISTER(Bar); This is also used for the Register class itself: Register.cpp class Register: public Object {...}; REGISTER(Register); The problem is that global() is called N times, but the new Register (*) is executed twice, instead of once. Investigating with some couts, I found out that - dunno why - the pointer _global is set to 0 after the first execution, in fact at the time of second execution it's created another object, which isn't deleted and is kept. Trying quickly with a debugger, it seems that at (**) a destructor for shared_ptr is called, and I think that it may be the cause of the issue, but I'm having an hard time figuring this out and *why* a destructor is called (maybe it's just for a temporary object). Looking at boost docs, i found out this: http://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/sp_techniques.html#stati... so I tried to use that null deleter, like this: struct nullDeleter { void operator()(void const *p) const { cout << "Trying to delete " << p << endl; } }; and used like: if (!_global) _global = RegisterP(new Register, nullDeleter()); The problem is that this is never called! The object is never destroyed by this, but somehow the static _global is set to 0 (only once), and thus allocated twice. I temporary solved this using a static raw pointer, which is allocated once and a shared_ptr is built with it: class Register { ... static RegisterP global() { if (!_global) _global = new Register; return RegisterP(_global, nullDeleter()); } ... static Register *_global; }; Which works. Any suggestion is welcome :) Thanks -- ~Ale
I see that you're declaring _global, but I don't see you defining it anywhere. Are you properly defining _global at file scope?
On Thu, Oct 16, 2008 at 9:27 PM, Adam Merz
I see that you're declaring _global, but I don't see you defining it anywhere. Are you properly defining _global at file scope?
Yes, I'm defining it with: RegisterP Register::_global; This should invoke the default constructor, right? Thanks -- ~Ale
Alessandro Re: ...
class Register; typedef shared_ptr<Register> RegisterP; class Register: public Object { public: Register(); void add(string name) { // Stuff with name } static RegisterP global() { if (!_global) _global = RegisterP(new Register); // * return _global; } private: static RegisterP _global; };
...
Yes, I'm defining it with: RegisterP Register::_global; This should invoke the default constructor, right?
The problem is that the default constructor may be invoked after the first call to Register::global, overwriting _global and resetting it to empty. The first subsequent call to global() would now reinitialize it. You can avoid this by making _global a local static: static RegisterP global() { static RegisterP _global( new Register ); return _global; }
On Fri, Oct 17, 2008 at 12:42 AM, Peter Dimov
The problem is that the default constructor may be invoked after the first call to Register::global, overwriting _global and resetting it to empty. The first subsequent call to global() would now reinitialize it.
You can avoid this by making _global a local static:
static RegisterP global() { static RegisterP _global( new Register ); return _global; }
Yes! You're right :) I just tried with a debugger and is as you say. Thanks! -- ~Ale
On Thursday 16 October 2008 18:42, Peter Dimov wrote:
The problem is that the default constructor may be invoked after the first call to Register::global, overwriting _global and resetting it to empty. The first subsequent call to global() would now reinitialize it.
You can avoid this by making _global a local static:
static RegisterP global() { static RegisterP _global( new Register ); return _global; }
Hmm, so I guess it is impossible to make a thread-safe singleton that avoids the static initialization order fiasco, unless your compiler guarantees thread-safe initialization of local statics. Even boost::call_once would require the once_flag to be statically initialized before the call_once call happens.
On Thursday 16 October 2008 22:56, Frank Mori Hess wrote:
Hmm, so I guess it is impossible to make a thread-safe singleton that avoids the static initialization order fiasco, unless your compiler guarantees thread-safe initialization of local statics. Even boost::call_once would require the once_flag to be statically initialized before the call_once call happens.
Err, nevermind. Apparently static POD types (once_flag) are guaranteed to be initialized before any constructors are run.
participants (4)
-
Adam Merz
-
Alessandro Re
-
Frank Mori Hess
-
Peter Dimov