I have the following classes that try to implement a generic Singleton.
struct BaseObject
{
virtual ~BaseObject() {}
};
class _helper
{
private:
template<typename T> friend class Singleton;
set<BaseObject*> _s;
static _helper& _get()
{
static _helper t;
return t;
}
_helper()
{
cout<<" _helper ctor"<<endl;
}
~_helper()
{
cout<<" _helper dtor"<<endl;
//assert(_s.empty());
}
};
// Singleton<foo>::Instance() returns a unique instance of foo
template <typename T>
class Singleton : virtual private T
{
public:
static T& Instance()
{
static Singleton<T> _T;
return _T;
}
private:
Singleton()
{
cout<<"inserting into helper "<<typeid(T).name()<<" ptr "<<this<<endl;
assert(!_helper::_get()._s.count(this));
_helper::_get()._s.insert(this);
}
~Singleton()
{
cout<<"erasing from helper "<<typeid(T).name()<<" ptr "<<this<<endl;
assert(_helper::_get()._s.count(this));
_helper::_get()._s.erase(this);
}
};
Now if I call Singleton< bar>::Instance()
followed by Singleton< foo>::Instance()
, I should see the following output:
inserting into helper 3bar ptr 0x509630
_helper ctor
inserting into helper 3foo ptr 0x509588
erasing from helper 3foo ptr 0x509588
erasing from helper 3bar ptr 0x509630
_helper dtor
However in some cases, I see the following:
inserting into helper 3bar ptr 0x509630
_helper ctor
inserting into helper 3foo ptr 0x509588
erasing from helper 3bar ptr 0x509630
_helper dtor
erasing from helper 3foo ptr 0x509588
Note that in the second case, bar
and foo
got destructed in the same order as they were constructed. This seems to happen when the foo
and bar
singletons are instantiated inside a shared library(.so) as static references:
static bar& b = Singleton<bar>::Instance();
static foo& f = Singleton<foo>::Instance();
Any ideas why it would do that?
This can happen if the singletons and the helper are located in different translation units, or different shared objects. Bear in mind that it is rather difficult to predict in which translation unit a template instance will end up. Also remember that each shared object can get its own instance of say Singleton<foo>::_T
. So you have some kind of per-shared-object singletons (not very useful IMHO).
Note that your helper is destroyed before the last object is removed from it. This will lead to program crash on exit. Yes this exact thing has happened to me. You will need to implement an object counter in the _helper class such that it is not destroyed until there's at least one object registered with it. Alternatively, allocate all singletons on the heap and let the helper destroy them when its lifetime ends.
update This destruction order reversal probably cannot happen if the two static objects are owned by the same dynamic library. It definitely can and does happen otherwise. Here programmers are advised against exporting static objects across dynamic library boundaries.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With