Pages: 1
- Accueil forums
- » Routines
- » C++11 Pattern Singleton Thread-safe et Variadic
#1
Pour l'utiliser, c'est simple:
Vous prenez une classe, vous la faites dériver de Singleton, le paramètre template est la classe elle-même, ensuite, si vous avez un constructeur par défaut sans comportement "spécial", utilisez la macro SINGLETON_DECLARE_DEFAULT_CONSTRUCTOR(LeNomDeLaClasse), si vous avez un constructeur par défaut avec comportement, utilisez la macro SINGLETON_DECLARE, si vous n'avez pas de constructeur par défaut, utilisez simplement SINGLETON_DECLARE_NO_DEFAULT_CONSTRUCTOR.
Enfin, utilisez la macro SINGLETON_POSSESS_DEFAULT_CONSTRUCTOR si votre classe possède un constructeur par défaut, sinon utilisez SINGLETON_UNPOSSESS_DEFAULT_CONSTRUCTOR, hors de la classe.
Un petit exemple:
Cette classe peut throw une exception runtime_error dans le cas où, vous faites appel à GetInstance() alors que la classe n'a pas de constructeur par défaut.
18-11-2012 18:06:29
- OzVessalius
- Membres
- Date d'inscription:
- Messages: 8
- IP: 82.229.30.182
- Courriel
Bonjour,
Voici une petite routine que j'ai fini il y a pas longtemps, il vous faudra un compilateur compatible C++11 qui puisse gérer les variadics template, les threads natif, les type traits. J'utilise g++ 4.7.2 comme compilateur. À ma connaissance, pour MSVC, il faudra avoir MSVC 2012 avec la dernière mise à jour.
Code c++ :
#ifndef SINGLETON_DEFINED_HPP
#define SINGLETON_DEFINED_HPP
#include <thread>
#include <memory>
#include <mutex>
#include <type_traits>
template<class T>
class Singleton
{
public:
virtual ~Singleton() { }
static T* GetInstance()
{
if (!m_instance)
{
instancier<T, std::is_default_constructible<T>::value>::instanciate(m_instance, m_onceFlag);
if (!m_instance) // K is not default constructible, crash detected.
throw std::runtime_error("Singleton<T> fatal error: K is not default constructible and we ask for construct him with none args... Crash detected.");
}
return m_instance.get();
}
template<typename... Args>
static T* GetInstance(Args... args)
{
auto f = &Singleton<T>::template _DoInit<Args...>;
std::call_once(m_onceFlag, f, args...);
return m_instance.get();
}
protected:
Singleton() { }
private:
template<typename K, bool opt>
struct instancier
{
static void instanciate(std::unique_ptr<K>& instance, std::once_flag& onceFlag)
{
auto f = [&instance]()
{
instance.reset(new K);
};
std::call_once(onceFlag, f);
}
};
template<typename K>
struct instancier<K, false>
{
static void instanciate(std::unique_ptr<K>& instance, std::once_flag& onceFlag)
{
}
};
template<typename... Args>
static void _DoInit(Args... args)
{
m_instance.reset(new T(std::forward<Args>(args)...));
}
private:
static std::unique_ptr<T> m_instance;
static std::once_flag m_onceFlag;
Singleton(const Singleton& src) = delete;
Singleton& operator=(const Singleton& rhs) = delete;
};
#define SINGLETON_DECLARE(className) friend class Singleton<className>; \\
className();
#define SINGLETON_DECLARE_DEFAULT_CONSTRUCTOR(className) friend class Singleton<className>; \\
className() { }
#define SINGLETON_DECLARE_NO_DEFAULT_CONSTRUCTOR(className) friend class Singleton<className>;
#define SINGLETON_POSSESS_DEFAULT_CONSTRUCTOR(className) namespace std { template<> struct is_default_constructible<className> : public std::integral_constant<bool, true> { }; };
#define SINGLETON_UNPOSSESS_DEFAULT_CONSTRUCTOR(className) namespace std { template<> struct is_default_constructible<className> : public std::integral_constant<bool, false> { }; };
template<class T>
std::unique_ptr<T> Singleton<T>::m_instance = nullptr;
template<class T>
std::once_flag Singleton<T>::m_onceFlag;
#endif
Pour l'utiliser, c'est simple:
Vous prenez une classe, vous la faites dériver de Singleton, le paramètre template est la classe elle-même, ensuite, si vous avez un constructeur par défaut sans comportement "spécial", utilisez la macro SINGLETON_DECLARE_DEFAULT_CONSTRUCTOR(LeNomDeLaClasse), si vous avez un constructeur par défaut avec comportement, utilisez la macro SINGLETON_DECLARE, si vous n'avez pas de constructeur par défaut, utilisez simplement SINGLETON_DECLARE_NO_DEFAULT_CONSTRUCTOR.
Enfin, utilisez la macro SINGLETON_POSSESS_DEFAULT_CONSTRUCTOR si votre classe possède un constructeur par défaut, sinon utilisez SINGLETON_UNPOSSESS_DEFAULT_CONSTRUCTOR, hors de la classe.
Un petit exemple:
Code c++ :
class Unique : public Singleton<Unique>
{
SINGLETON_DECLARE_DEFAULT_CONSTRUCTOR(Unique)
public:
void helloWorld() { std::cout << "Hello, World!" << std::endl; }
};
SINGLETON_POSSESS_DEFAULT_CONSTRUCTOR(Unique)
Unique::GetInstance()->helloWorld();
Cette classe peut throw une exception runtime_error dans le cas où, vous faites appel à GetInstance() alors que la classe n'a pas de constructeur par défaut.
Hors ligne
#2
+utiliser la nouvelle norme, bonne effort
bon après je peut toujours discuter des Singletons, mes c'est plus de l'opinion personnel
18-11-2012 22:03:37
- Magun
- Administrateurs

- Date d'inscription:
- Messages: 910
- IP: 83.203.3.168
- Courriel Site web
merci pour la routine, je note juste:
- l'utilisation de friend n'est pas nécéssaire, les cas ou le constructeur ne soit pas public est plutot rare
- utiliser des macros je trouve pas ça très propre
+utiliser la nouvelle norme, bonne effort
bon après je peut toujours discuter des Singletons, mes c'est plus de l'opinion personnel
Hors ligne
#3
18-11-2012 22:54:52
- OzVessalius
- Membres
- Date d'inscription:
- Messages: 8
- IP: 82.229.30.182
- Courriel
Uhm, en faite, le constructeur n'est pas publique pour le simple fait que c'est un pattern Singleton ^^
Donc, c'est un peu normal ![]()
Si le constructeur est publique, on pourrait créer plus d'une instance, ça viole le pattern.
L'utilisation des macros, c'est pour simplifier l'utilisation, mais on peut s'en passer.
Hors ligne



