From: Manuel Nickschas Date: Fri, 28 Sep 2018 19:17:32 +0000 (+0200) Subject: common: Provide Singleton mixin for handling pseudo singletons X-Git-Tag: 0.13-rc2~16 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=d19063654aa5fc2fd4cf7c111aac00803cf7c845 common: Provide Singleton mixin for handling pseudo singletons We have several singleton and pseudo-singleton classes in Quassel. A pseudo singleton can be constructed and destroyed in a controlled manner, but can be accessed through a static instance pointer while it is alive. As an example, QCoreApplication behaves like this. In order to avoid duplication and unify the behavior of our singletons in the future, provide a mixin. Classes can inherit from this and gain an instance() method, as well as protection against multiple instantiation and access outside of the object's lifetime. --- diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d9bc71af..72227605 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -40,6 +40,7 @@ set(SOURCES remotepeer.cpp settings.cpp signalproxy.cpp + singleton.h syncableobject.cpp transfer.cpp transfermanager.cpp diff --git a/src/common/singleton.h b/src/common/singleton.h new file mode 100644 index 00000000..b3e4dfd3 --- /dev/null +++ b/src/common/singleton.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2005-2018 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#pragma once + +#include + +/** + * Mixin class for "pseudo" singletons. + * + * Classes inheriting from this mixin become "pseudo" singletons that can still be constructed + * and destroyed in a controlled manner, but also gain an instance() method for static access. + * This is very similar to the behavior of e.g. QCoreApplication. + * + * The mixin protects against multiple instantiation, use-before-instantiation and use-after-destruction + * by aborting the program. This is intended to find lifetime issues during development; abort() + * produces a backtrace that makes it easy to find the culprit. + * + * The Curiously Recurring Template Pattern (CRTP) is used for the mixin to be able to provide a + * correctly typed instance pointer. + */ +template +class Singleton +{ +public: + /** + * Constructs the mixin. + * + * The constructor can only be called once; subsequent invocations abort the program. + * + * @param instance Pointer to the instance being created, i.e. the 'this' pointer of the parent class + */ + Singleton(T *instance) + { + if (_destroyed) { + qFatal("Trying to reinstantiate a destroyed singleton, this must not happen!"); + abort(); // This produces a backtrace, which is highly useful for finding the culprit + } + if (_instance) { + qFatal("Trying to reinstantiate a singleton that is already instantiated, this must not happen!"); + abort(); + } + _instance = instance; + } + + // Satisfy Rule of Five + Singleton(const Singleton &) = delete; + Singleton(Singleton &&) = delete; + Singleton &operator=(const Singleton &) = delete; + Singleton &operator=(Singleton &&) = delete; + + /** + * Destructor. + * + * Sets the instance pointer to null and flags the destruction, so a subsequent reinstantiation will fail. + */ + ~Singleton() + { + _instance = nullptr; + _destroyed = true; + } + + /** + * Accesses the instance pointer. + * + * If the singleton hasn't been instantiated yet, the program is aborted. No lazy instantiation takes place, + * because the singleton's lifetime shall be explicitly controlled. + * + * @returns A pointer to the instance + */ + static T *instance() + { + if (_instance) { + return _instance; + } + qFatal("Trying to access a singleton that has not been instantiated yet"); + abort(); + } + +private: + static T *_instance; + static bool _destroyed; + +}; + +template +T *Singleton::_instance{nullptr}; + +template +bool Singleton::_destroyed{false};