Reputation: 199
Singletons in C++ (at least prior C++11 AFAIK) can be a nightmare. With the whole static initialisation order fiasco. But boost::call_once seems to offer a robust way to implement singletons. I've tried to come up with an easy to use idiom I'd like to share for some critical feedback, hope I haven't been completely foolish :)
// Usage:
// You can make anything with a public or protected ctor a singleton.
//
// class A
// {
// public:
// ~A();
// public:
// // Optional, shorter to type
// static A& Instance() { return Singleton<A>::Instance(); }
// void foo();
//
// protected:
// explicit A(); // Can't be private, but can be public, protected is recommended.
// };
//
// Singleton<A>::Instance().foo();
// A::Instance().foo();
//
template< class T >
class Singleton : public T // inerits from T so we can access the protected constructor.
{
public:
virtual ~Singleton() {}
public:
static T& Instance();
private:
static boost::once_flag s_onceFlag;
// We use a raw pointer to avoid dynamic initialisation. If something that
// has a constructor (eg, shared_ptr ) then the dynamically initialised ctor
// may get called after the call once function is called (if the call once function
// is statically invoked before main). Then the instance pointer will be overwritten.
// Using a zero initialised pointer avoids this problem.
static T* s_instance;
private:
static void Init();
private:
explicit Singleton() {} // Used only to allow access to the protected ctor in base.
};
template< class T >
boost::once_flag Singleton<T>::s_onceFlag = BOOST_ONCE_INIT; // zero initialised variable, no order o initialisation shananigans
template< class T >
T* Singleton<T>::s_instance = 0;
template< class T >
void Singleton<T>::Init()
{
static Singleton<T> instance; // static local variable is thread safe since this function is called once only.
s_instance = &instance;
}
template< class T >
T& Singleton<T>::Instance()
{
boost::call_once( s_onceFlag, Init );
return *s_instance;
}
Upvotes: 2
Views: 3719
Reputation: 393487
To be fair, I expect that call_once
is a threading specific version of something that can already been had in c++11 using just a function local static initializer:
template< class T >
T& Singleton<T>::Instance()
{
static bool const init = [] () -> bool { Init(); return true; }();
return *s_instance;
}
The initializer is guaranteed not to race (in the language specification).
Worst case behaviour is when the initializer throws: the exception will propagate out of the Instance()
method but next time will try to initialize again, because the specification deems the variable to have "not been initialized" when the initializer expression throws.
If in fact you "need" a thread-aware singleton (e.g. instance per thread), then you may want the thread_local
keyword, with largely the same semantics.
Upvotes: 4