c++ - Qt Singleton implementation -
i looking singleton qt implementation , found this. have question it.
- what purpose of making
create
qbasicatomicpointer
? - what point in
qcallonce
of usingtestandsetrelaxed
if have used fetchandstoreacquire ? isn't acquire semantic preventing memory reordering after ? - what purpose of
qcallonceperthread
function ? isn'tqcallonce
thread-safe ?
i copy contents of suggested implementation here:
call_once.h
#ifndef call_once_h #define call_once_h #include <qtglobal> #include <qatomicint> #include <qmutex> #include <qwaitcondition> #include <qthreadstorage> #include <qthread> namespace callonce { enum ecallonce { co_request, co_inprogress, co_finished }; q_global_static(qthreadstorage<qatomicint*>, once_flag) } template <class function> inline static void qcallonce(function func, qbasicatomicint& flag) { using namespace callonce; #if qt_version < 0x050000 int protectflag = flag.fetchandstoreacquire(flag); #elif qt_version >= 0x050000 int protectflag = flag.fetchandstoreacquire(flag.load()); #endif if (protectflag == co_finished) return; if (protectflag == co_request && flag.testandsetrelaxed(protectflag, co_inprogress)) { func(); flag.fetchandstorerelease(co_finished); } else { { qthread::yieldcurrentthread(); } while (!flag.testandsetacquire(co_finished, co_finished)); } } template <class function> inline static void qcallonceperthread(function func) { using namespace callonce; if (!once_flag()->haslocaldata()) { once_flag()->setlocaldata(new qatomicint(co_request)); qcallonce(func, *once_flag()->localdata()); } } #endif // call_once_h
singleton.h
#ifndef singleton_h #define singleton_h #include <qtglobal> #include <qscopedpointer> #include "call_once.h" template <class t> class singleton { private: typedef t* (*createinstancefunction)(); public: static t* instance(createinstancefunction create); private: static void init(); singleton(); ~singleton(); q_disable_copy(singleton) static qbasicatomicpointer<void> create; static qbasicatomicint flag; static qbasicatomicpointer<void> tptr; bool inited; }; template <class t> t* singleton<t>::instance(createinstancefunction create) { singleton::create.store(create); qcallonce(init, flag); return (t*)tptr.load(); } template <class t> void singleton<t>::init() { static singleton singleton; if (singleton.inited) { createinstancefunction createfunction = (createinstancefunction)singleton::create.load(); tptr.store(createfunction()); } } template <class t> singleton<t>::singleton() { inited = true; }; template <class t> singleton<t>::~singleton() { t* createdtptr = (t*)tptr.fetchandstoreordered(nullptr); if (createdtptr) { delete createdtptr; } create.store(nullptr); } template<class t> qbasicatomicpointer<void> singleton<t>::create = q_basic_atomic_initializer(nullptr); template<class t> qbasicatomicint singleton<t>::flag = q_basic_atomic_initializer(callonce::co_request); template<class t> qbasicatomicpointer<void> singleton<t>::tptr = q_basic_atomic_initializer(nullptr); #endif // singleton_h
how use
// myclass.h #ifndef myclass_h #define myclass_h #include <qobject> class myclass : public qobject { q_object private: myclass(qobject* parent = 0); static myclass* createinstance(); public: ~myclass(); static myclass* instance(); }; #endif // myclass_h // myclass.cpp #ifndef myclass_h #define myclass_h #include <qobject> #include "singleton.h" myclass::myclass(qobject* parent): qobject(parent) { } myclass* myclass::createinstance() { return new myclass(); } myclass::~myclass() { } myclass* myclass::instance() { return singleton<myclass>::instance(myclass::createinstance); } #endif // myclass_h
main.cpp
#include <qtextstream> #include "myclass.h" #define myclassinstance singleton<myclass>::instance() int main(int argc, char* argv[]) { qtextstream(stdout) << myclass::instance()->metaobject()->classname() << endl; return 0; }
i think enough use next singleton implementation. remember, c++11 gurantees there 1 instancing/initialization static variable. original problem in case, when more 1 thread tries access instance in same time , there possible situation, when singleton created twice.
template <typename t, typename d = t> class singleton { friend d; static_assert(std::is_base_of_v<t, d>, "t should base type d"); public: static t& instance(); private: singleton() = default; ~singleton() = default; singleton( const singleton& ) = delete; singleton& operator=( const singleton& ) = delete; }; template <typename t, typename d> t& singleton<t, d>::instance() { static d inst; return inst; } // usage: class myclass : public singleton<myclass> { public: void foo(){} }; // access: myclass::instance().foo();
Comments
Post a Comment