Commit 8f0edccf authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Rework our Singleton to provide multiple policy.

This is mostly aiming at allowing Core to be destroyed within the scope
of main()
parent efa9ea74
......@@ -66,7 +66,7 @@ class MLTBackend : public IBackend, public Singleton<MLTBackend>
std::map<std::string, IFilterInfo*> m_availableFilters;
friend Singleton<MLTBackend>;
friend Singleton_t::AllowInstantiation;
};
} // MLT
......
......@@ -43,7 +43,7 @@ private:
QTranslator *m_translator;
QTranslator *m_qtTranslator;
friend class Singleton<LanguageHelper>;
friend Singleton_t::AllowInstantiation;
};
#endif // LANGUAGEHELPER_H
......@@ -63,7 +63,7 @@ class NotificationZone : public QWidget, public Singleton<NotificationZone>
private slots:
void hideNotification();
friend class Singleton<NotificationZone>;
friend Singleton_t::AllowInstantiation;
};
#endif // NOTIFICATIONZONE_H
......@@ -166,10 +166,3 @@ Core::runtime()
{
return m_timer.elapsed();
}
Core*
Core::instance()
{
static Core core;
return &core;
}
......@@ -44,8 +44,9 @@ namespace Commands
}
#include <QElapsedTimer>
#include "Tools/Singleton.hpp"
class Core
class Core : public ScopedSingleton<Core>
{
public:
Backend::IBackend* backend();
......@@ -65,8 +66,6 @@ class Core
bool loadProject( const QString& fileName );
bool newProject( const QString& projectName, const QString& projectPath );
static Core* instance();
private:
Core();
~Core();
......@@ -85,6 +84,8 @@ class Core
Commands::AbstractUndoStack* m_undoStack;
Library* m_library;
QElapsedTimer m_timer;
friend Singleton_t::AllowInstantiation;
};
#endif // CORE_H
......@@ -94,6 +94,7 @@ VLMCGuimain( int argc, char **argv )
Backend::IBackend* backend;
VLMCmainCommon( app, &backend );
auto coreLock = Core::Policy_t::lock();
/* Load a project file */
bool project = false;
......@@ -186,6 +187,7 @@ VLMCCoremain( int argc, char **argv )
Backend::IBackend* backend;
VLMCmainCommon( app, &backend );
auto coreLock = Core::Policy_t::lock();
/* Load a project file */
if ( app.arguments().count() < 3 )
......
......@@ -31,40 +31,149 @@
#ifndef SINGLETON_HPP
#define SINGLETON_HPP
#include <stdlib.h>
#include <memory>
#include <mutex>
namespace details
{
namespace policy
{
template <typename T>
class Singleton
struct OldPolicy
{
public:
static T* instance()
static T* get()
{
if ( m_instance == nullptr )
m_instance = new T;
return m_instance;
if (s_instance == nullptr)
s_instance = new T;
return s_instance;
}
static void destroyInstance()
static void release()
{
if ( m_instance != nullptr )
delete s_instance;
s_instance = nullptr;
}
using InstanceType = T*;
private:
static T* s_instance;
};
template <typename T>
T* OldPolicy<T>::s_instance = nullptr;
template <typename T>
struct MeyersPolicy
{
static T* get()
{
static T inst;
return &inst;
}
// No release method; automatically released when program exits
};
template <typename T>
class ScopedPolicy
{
struct Lock
{
std::shared_ptr<T> ptr;
friend class ScopedPolicy;
};
public:
static Lock lock()
{
auto inst = s_weak_inst.lock();
if ( inst )
return Lock{ inst };
std::unique_lock<std::mutex> lock( s_mutex );
inst = s_weak_inst.lock();
if ( !inst )
{
delete m_instance;
m_instance = nullptr;
// use a lambda expression since the shared_ptr doesn't have access to T::~T()
s_weak_inst = inst = std::shared_ptr<T>( new T, [](T*ptr){ delete ptr; } );
}
return Lock{ inst };
}
static std::shared_ptr<T> get()
{
return lock().ptr;
}
static std::mutex s_mutex;
static std::weak_ptr<T> s_weak_inst;
};
template <typename T>
std::mutex ScopedPolicy<T>::s_mutex;
template <typename T>
std::weak_ptr<T> ScopedPolicy<T>::s_weak_inst;
}
template <typename T, typename Policy>
class PSingleton
{
public:
using Singleton_t = PSingleton<T, Policy>;
/// Meant to be used by Singleton<> users to declare the Policy as a friend class.
/// The AllowInstantiation just reads better than Singleton<T>::Policy_t
using AllowInstantiation = Policy;
using Policy_t = Policy;
static auto instance() -> decltype(Policy::get())
{
return Policy::get();
}
static void destroyInstance()
{
Policy::release();
}
protected:
Singleton(){}
~Singleton() = default;
Singleton(const Singleton<T>&) = delete;
Singleton(Singleton<T>&&) = delete;
Singleton<T>& operator=(const Singleton<T>&) = delete;
Singleton<T>& operator=(Singleton<T>&&) = delete;
protected:
static T* m_instance;
PSingleton(){}
~PSingleton() = default;
public:
PSingleton(const Singleton_t&) = delete;
PSingleton(Singleton_t&&) = delete;
Singleton_t& operator=(const Singleton_t&) = delete;
Singleton_t& operator=(Singleton_t&&) = delete;
};
}
/**
* @brief Soingleton defines the "old" way of having a singleton.
* ie. by having to call instance()/destroyInstance() couple
* @warning This isn't thread safe
*/
template <typename T>
using Singleton = details::PSingleton<T, details::policy::OldPolicy<T>>;
/**
* @brief MeyersPolicy defines the usual C++11 singleton
* This is threadsafe, but doesn't let you control the moment the instance will be
* released
*/
template <typename T>
T* Singleton<T>::m_instance = nullptr;
using MeyersSingleton = details::PSingleton<T, details::policy::MeyersPolicy<T>>;
/**
* @brief Defined a singleton that will be released when the originally acquied "lock"
* falls out of scope
* Inherithing from this singleton implementation will provide a T::Policy_t::lock() method
* which needs to be called to acquire a lock.
* When this lock falls out of scope, any subsequent call to lock()/instance() will create a new instance.
* Calling instance() without having a lock instantiated will create a new instance that will get destroyed
* when the returned instance falls out of scope.
*/
template <typename T>
using ScopedSingleton = details::PSingleton<T, details::policy::ScopedPolicy<T>>;
#endif // SINGLETON_HPP
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment