#0 

18-11-2012 19:06:29

OzVessalius
Petit nouveau
Date d'inscription: 18-11-2012
Messages: 8

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;
};                             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 << "Singleton<T> fatal error: K is not default constructible and we ask for construct him with none args... Crash detected." << 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


#1 

18-11-2012 23:03:37

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 907
Corrections: 2
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 wink

Hors ligne


#2 

18-11-2012 23:54:52

OzVessalius
Petit nouveau
Date d'inscription: 18-11-2012
Messages: 8

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 smile
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


Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
881 membres
1426 sujets
11116 messages
Dernier membre inscrit: Bidule
32 invités en ligne
Aucun membre connecté
RSS Feed