IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

factory, template, et enregistrement des objets


Sujet :

Langage C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut factory, template, et enregistrement des objets
    Bonjour,

    J'ai écrit une classe singleton de fabrique générique, j'utilise ensuite une classe de base pour l'enregistrement des objets, et je voulais que cet enregistrement se fasse par une macro appelant une fonction privée de la classe de base.

    Seulement, j'ai une erreur de compilation dans la fonction d'enregistrement dont je n'arrive pas à voir la cause.

    ObjectFactory.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    template<typename T_ID, typename T_BASE>
    class ObjectFactory : public QSharedData
    {
        typedef T_BASE* (ClassCreationFunc) ();
        QHash<T_ID, ClassCreationFunc*> m_registry;
        static QAtomicPointer<ObjectFactory> m_instance;
        static QMutex m_mutex;
     
    public :
        template<typename T_OBJECT>
        static T_BASE* derivedClassCreationFunc()
        {
            T_OBJECT* t = new T_OBJECT;
            T_BASE* c = dynamic_cast<T_BASE*>(t);
            if (!c) delete t;
            return c;
        }
     
        static bool registerObject(T_ID id, ClassCreationFunc* func)
        {
            typename QHash<T_ID, ClassCreationFunc*>::iterator it =m_registry.insert(std::pair<T_ID, ClassCreationFunc*>(id, func));
            return(it != m_registry.end());
        }
     
        T_BASE* create(T_ID id)
        {
            T_BASE* obj = 0;
            typename QHash<T_ID, ClassCreationFunc*>::iterator it = m_registry.find(id);
            if (it != m_registry.end()) {
              ClassCreationFunc* pFunc = it->second;
              obj = pFunc();
            }
            return obj;
        }
     
        static QSharedDataPointer<ObjectFactory> getInstance()
        {
            if(!m_instance) {
                QMutexLocker locker(&m_mutex);
                if(!m_instance) {
     
                }
            }
            return m_instance;
        }
     
    #endif // OBJECTFACTORY_H
    AbstractObject.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class AbstractObject
    {
    private :
        template<typename T_ID,typename T_OBJECT>
        bool _register_object(T_ID id);
    public:
        AbstractObject();
        virtual ~AbstractObject();
     
    #define REGISTER_OBJECT(T_ID,id,T_OBJECT) bool _FACTORY_REGISTERED_ = _register_object<T_ID,T_OBJECT>(id);
    };
    Le code qui pose problème, AbstractObject.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template<typename T_ID, typename T_OBJECT>
    bool AbstractObject::_register_object(T_ID id)
    {
        ObjectFactory<T_ID,AbstractObject> fac = ObjectFactory<T_ID,AbstractObject>::getInstance();
     
    // la ligne qui provoque l'erreur :    
        return fac.registerObject(id, ObjectFactory<T_ID, AbstractObject>::derivedClassCreationFunc<T_OBJECT>);
    }
    Et le message du compilateur :
    abstractobject.cpp: error: expected primary-expression before '>' token
    J'ai retourné le problème dans tous les sens, et je n'arrive pas à voir d'où viens cette erreur.

    Si quelqu'un pouvait m'aiguiller, je lui en serais reconnaissant

    Merci par avance.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    le code template ne doit pas se trouver dans un .cpp.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Merci pour cette réponse, mais cela ne change rien.

    D'ailleurs, à la base, ce code était dans le .h, je l'ai bougé pour faire d'autres essais qui ne se sont pas révélés concluant.

  4. #4
    Membre averti Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Points : 323
    Points
    323
    Par défaut
    C'est peut-être juste une erreur de copier/coller, mais il manque une accolade fermante a la déclaration de ObjectFactory.

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Effectivement, c'est une erreur de copier-coller

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Ensuite, je trouve ton singleton un peu étrange puisqu'il retourne une copie.
    En général un singleton retourne une référence, ou un pointeur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    static MySingleton& getInstance();
    Pour les membres static, on préfèrera le préfixe s_, laissant le m_ aux membres à part entière.

    Le code mis dans le .h déclenche la même erreur ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Ensuite, je trouve ton singleton un peu étrange puisqu'il retourne une copie.
    En général un singleton retourne une référence, ou un pointeur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    static MySingleton& getInstance();
    Il retourne un QSharedDataPointer<ObjectFactory>, qui est un pointeur intelligent de Qt.

    Citation Envoyé par Bousk Voir le message
    Pour les membres static, on préfèrera le préfixe s_, laissant le m_ aux membres à part entière.
    Noté.

    Citation Envoyé par Bousk Voir le message
    Le code mis dans le .h déclenche la même erreur ?
    Oui...


    EDIT : d'ailleurs, autre faute de copier-coller, le QHash<T_ID, ClassCreationFunc*> m_registry est static.

  8. #8
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Salut,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return fac.registerObject(id, ObjectFactory<T_ID, AbstractObject>::derivedClassCreationFunc<T_OBJECT>());

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Citation Envoyé par Trademark Voir le message
    Salut,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return fac.registerObject(id, ObjectFactory<T_ID, AbstractObject>::derivedClassCreationFunc<T_OBJECT>());
    Merci, mais cela ne change rien. Le problème ne vient pas de là.

  10. #10
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    return fac.registerObject(id, ObjectFactory<T_ID, AbstractObject>::template derivedClassCreationFunc<T_OBJECT>);
    Comme ObjectFactory<T_ID, AbstractObject> dépend d'un paramètre template il faut que tu lui indiques que ce qui suit est aussi un template.

  11. #11
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    return fac.registerObject(id, ObjectFactory<T_ID, AbstractObject>::template derivedClassCreationFunc<T_OBJECT>);
    Comme ObjectFactory<T_ID, AbstractObject> dépend d'un paramètre template il faut que tu lui indiques que ce qui suit est aussi un template.
    Merci!

    C'était bien ça

    Par contre, maintenant, j'ai un problème avec la macro REGISTER_OBJECT de AbstractObject.

    J'ai essayé de mettre en static la fonction _register_object(), mais cela ne fonctionne toujours pas.

    Nouveau AbstractObject.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class AbstractObject
    {
    protected :
        const bool m_FACTORY_REGISTERED_;
        template<typename T_ID, typename T_OBJECT>
        static bool _register_object(T_ID id)
        {
            ObjectFactory<T_ID,AbstractObject> fac = ObjectFactory<T_ID,AbstractObject>::getInstance();
            return fac.registerObject(id, ObjectFactory<T_ID, AbstractObject>::template derivedClassCreationFunc<T_OBJECT>());
        }
    public:
        AbstractObject();
        virtual ~AbstractObject();
     
    #define REGISTER_OBJECT(T_ID,id,T_OBJECT) m_FACTORY_REGISTERED_ = AbstractObject::_register_object<T_ID,T_OBJECT>(id);
    };
    TestObject.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class TestObject : public AbstractObject
    {
        REGISTER_OBJECT(QString, "TestObject",TestObject)
    public:
        TestObject();
    };
     
    #endif // TESTOBJECT_H
    Le compilateur me dit :
    testobject.h:8: error: '_register_object<QString, TestObject>' cannot appear in a constant-expression

  12. #12
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Salut,

    Tu ne peux pas affecter une valeur directement dans la classe. Tu dois faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TestObject() : m_FACTORY_REGISTERED_(AbstractObject::_register_object<QString,TestObject>("TestObject")) {}

  13. #13
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Citation Envoyé par Trademark Voir le message
    Salut,

    Tu ne peux pas affecter une valeur directement dans la classe. Tu dois faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TestObject() : m_FACTORY_REGISTERED_(AbstractObject::_register_object<QString,TestObject>("TestObject")) {}
    Salut,

    Le problème si je fais ça, c'est que je suis obliger d'instancier un objet TestObject avant de pouvoir utiliser la factory.

    Ce que je voudrais en fait, c'est une macro à mettre dans la classe dérivée d'AbstractObject qui lors de l'initialisation dynamique des objets, enregistre la classe dans la factory.

  14. #14
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Si je comprend bien, m_FACTORY_REGISTERED_ devrait être static car il n'en faudra que un par type d'objet. Et tu pourras l'initialiser dans le .cpp. Ce qui donnerait éventuellement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class TestObject : public AbstractObject
    {
        static const bool m_FACTORY_REGISTERED_;
    public:
        TestObject();
    };
    et dans le .cpp :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const bool TestObj::m_FACTORY_REGISTERED_ = AbstractObject::_register_object<QString,TestObject>("TestObject");
    Il faudra alors que toutes tes classes aient un membre static m_FACTORY_REGISTERED_. Ce qui est normal car c'est une variable de classe donc liée à ta classe. Et ce genre de chose ne s'hérite pas (qu'on me corrige si je me trompe).

  15. #15
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Citation Envoyé par Trademark Voir le message
    Si je comprend bien, m_FACTORY_REGISTERED_ devrait être static car il n'en faudra que un par type d'objet. Et tu pourras l'initialiser dans le .cpp. Ce qui donnerait éventuellement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class TestObject : public AbstractObject
    {
        static const bool m_FACTORY_REGISTERED_;
    public:
        TestObject();
    };
    et dans le .cpp :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const bool TestObj::m_FACTORY_REGISTERED_ = AbstractObject::_register_object<QString,TestObject>("TestObject");
    Il faudra alors que toutes tes classes aient un membre static m_FACTORY_REGISTERED_. Ce qui est normal car c'est une variable de classe donc liée à ta classe. Et ce genre de chose ne s'hérite pas (qu'on me corrige si je me trompe).
    Cette solution est viable, mais elle oblige à faire deux déclarations dans le code de la classe dérivée. J'aimerai arriver à ce qu'il n'y ai que l'héritage et une macro à spécifier.

    J'ai essayé une autre solution avec une classe dédiée à l'enregistrement des objets. Ce n'est pas mieux, j'ai des problèmes au niveau de l'édition des liens.

    ObjectFactory.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #ifndef OBJECTFACTORY_H
    #define OBJECTFACTORY_H
     
    #include <QHash>
    #include <QSharedDataPointer>
    #include <QMutex>
    #include <QMutexLocker>
     
     
    template<typename T_ID, typename T_BASE>
    class ObjectFactory : public QSharedData
    {
        typedef T_BASE* (ClassCreationFunc)();
        typedef ObjectFactory<T_ID,T_BASE> factory;
        static QHash<T_ID, ClassCreationFunc*> s_registry;
        static QSharedDataPointer<ObjectFactory> s_instance;
        static QMutex s_mutex;
     
    public :
        template<typename T_OBJECT>
        static T_BASE* derivedClassCreationFunc()
        {
            T_OBJECT* t = new T_OBJECT;
            T_BASE* c = dynamic_cast<T_BASE*>(t);
            if (!c) delete t;
            return c;
        }
     
        static inline bool registerObject(const T_ID& id, ClassCreationFunc* func)
        {
            typename QHash<T_ID, ClassCreationFunc*>::iterator it = s_registry.insert(id, func);
            return(it != s_registry.end());
        }
     
        inline T_BASE* create(const T_ID& id)
        {
            T_BASE* obj = 0;
            typename QHash<T_ID, ClassCreationFunc*>::iterator it = s_registry.find(id);
            if (it != s_registry.end()) {
              ClassCreationFunc* pFunc = it->second;
              obj = pFunc();
            }
            return obj;
        }
     
        static QSharedDataPointer<ObjectFactory<T_ID, T_BASE> > getInstance()
        {
            if(!factory::s_instance) {
                QMutexLocker locker(&factory::s_mutex);
                if(!factory::s_instance) {
                    factory::s_instance = new ObjectFactory<T_ID,T_BASE>();
                }
            }
            return ObjectFactory<T_ID, T_BASE>::s_instance;
        }
     
    #define FACTORY_REGISTER_CLASS(T_ID, T_Base, _id_, T_OBJECT) \
       QSharedDataPointer<ObjectFactory<T_ID,T_Base> > _fac_ = ObjectFactory<T_ID,T_Base>::getInstance(); \
       return _fac_->registerObject(_id_, ObjectFactory<T_ID, T_Base>::template derivedClassCreationFunc<T_OBJECT>);
    };
     
    #endif // OBJECTFACTORY_H
    FactoryRegister.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #ifndef FACTORYREGISTER_H
    #define FACTORYREGISTER_H
     
    #include "objectfactory.h"
     
    class FactoryRegister : public QSharedData
    {
    public:
        FactoryRegister();
        static bool s_registered;
     
        template<typename T_ID, typename T_BASE, typename T_OBJECT>
        static bool registerObject(const T_ID& id) {
            FACTORY_REGISTER_CLASS(T_ID, T_BASE, id, T_OBJECT)
        }
    };
     
    #endif // FACTORYREGISTER_H
    AbstractObject.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #ifndef ABSTRACTOBJECT_H
    #define ABSTRACTOBJECT_H
    #include "factoryregister.h"
     
    class AbstractObject
    {
    public:
        AbstractObject();
        virtual ~AbstractObject();
     
    #define REGISTER_OBJECT(T_ID,id,T_OBJECT) bool FactoryRegister::s_registered = FactoryRegister::registerObject<T_ID,AbstractObject,T_OBJECT>(id);
    };
     
    #endif // ABSTRACTOBJECT_H
    TestObject.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #ifndef TESTOBJECT_H
    #define TESTOBJECT_H
     
    #include "abstractobject.h"
     
    class TestObject : public AbstractObject
    {
    public:
        TestObject();
    };
     
    #endif // TESTOBJECT_H
    testobject.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #include "testobject.h"
     
    REGISTER_OBJECT(QString,"TestObject",TestObject)
    Et le résultat après édition des liens :
    objectfactory.h:48: undefined reference to `ObjectFactory<QString, AbstractObject>::s_instance'
    objectfactory.h:49: undefined reference to `ObjectFactory<QString, AbstractObject>::s_mutex'
    objectfactory.h:50: undefined reference to `ObjectFactory<QString, AbstractObject>::s_instance'
    objectfactory.h:51: undefined reference to `ObjectFactory<QString, AbstractObject>::s_instance'
    objectfactory.h:54: undefined reference to `ObjectFactory<QString, AbstractObject>::s_instance'
    objectfactory.h:23: undefined reference to `TestObject::TestObject()'
    objectfactory.h:31: undefined reference to `ObjectFactory<QString, AbstractObject>::s_registry'
    objectfactory.h:32: undefined reference to `ObjectFactory<QString, AbstractObject>::s_registry'

  16. #16
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Je n'ai pas lu ton code, mais je viens de penser, pourquoi ne pas imaginer une fonction [codeinline]template<typename T> static bool AbstractObject::is_registered()[codeinline] au lieu de stocker un booléen ?

  17. #17
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Après une activité chargée qui m'a tenu éloigné de ce petit projet, je reviens avec une solution à mon problème,et l'enregistrement dans les classes est encore plus simple que ce que j'espérai faire au départ

    J'ai utilisé deux classes pour faire l'auto-enregistrement des objets. La première hérite du type de base et possède un membre statique de la deuxième, cette dernière effectuant l'enregistrement dans la factory dans le constructeur.

    ObjectFactory.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    #ifndef OBJECTFACTORY_H
    #define OBJECTFACTORY_H
     
    #include <QHash>
    #include <QSharedDataPointer>
    #include <QMutex>
    #include <QMutexLocker>
     
     
    template<typename T_ID, typename T_BASE>
    class ObjectFactory : public QSharedData
    {
        typedef T_BASE* (ClassCreationFunc)();
        typedef ClassCreationFunc* p_ClassCreationFunc;
        typedef ObjectFactory<T_ID,T_BASE> factory;
        typedef QHash<T_ID, p_ClassCreationFunc> factoryQHash;
        typedef QSharedDataPointer<ObjectFactory<T_ID, T_BASE> > p_factory;
     
        static factoryQHash s_registry;
        static p_factory s_instance;
        static QMutex s_mutex;
     
    public :
        template<typename T_OBJECT>
        static T_BASE* derivedClassCreationFunc()
        {
            T_OBJECT* t = new T_OBJECT;
            T_BASE* c = dynamic_cast<T_BASE*>(t);
            if (!c) delete t;
            return c;
        }
     
        static inline void registerObject(const T_ID& id, ClassCreationFunc* func)
        {
            s_registry.insert(id, func);
        }
     
        inline T_BASE* create(const T_ID& id)
        {
            T_BASE* obj = 0;
            typename factoryQHash::iterator it = s_registry.find(id);
            if (it != s_registry.end()) {
              ClassCreationFunc* pFunc = it.value();
              obj = pFunc();
            }
            return obj;
        }
     
        static p_factory getInstance()
        {
            if(!factory::s_instance) {
                QMutexLocker locker(&factory::s_mutex);
                if(!factory::s_instance) {
                    factory::s_instance = new ObjectFactory<T_ID,T_BASE>();
                }
            }
            return ObjectFactory<T_ID, T_BASE>::s_instance;
        }
    };
    template<typename T_ID, typename T_BASE> typename ObjectFactory<T_ID, T_BASE>::factoryQHash ObjectFactory<T_ID, T_BASE>::s_registry;
    template<typename T_ID, typename T_BASE> typename ObjectFactory<T_ID, T_BASE>::p_factory ObjectFactory<T_ID, T_BASE>::s_instance;
    template<typename T_ID, typename T_BASE> QMutex ObjectFactory<T_ID,T_BASE>::s_mutex;
     
    #endif // OBJECTFACTORY_H
    FactoryRegister.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     
    #ifndef FACTORYREGISTER_H
    #define FACTORYREGISTER_H
     
    #include "objectfactory.h"
     
    #define _TYPETOSTRING_(Classname) #Classname
     
    template<typename typeBase, typename ClassName>
    struct FactoryRegister
    {
        typedef ObjectFactory<QString,typeBase> objectFactory;
        typedef FactoryRegister<typeBase,ClassName> factoryRegister;
     
        FactoryRegister() {
            QSharedDataPointer<objectFactory> fac = objectFactory::getInstance();
            fac->registerObject(_TYPETOSTRING_(Classname), objectFactory::template derivedClassCreationFunc<ClassName>);
        }
    };
     
    template<typename typeBase, typename ClassName>
    struct AutoRegister : public typeBase
    {
        typedef FactoryRegister<typeBase,ClassName> factoryRegister;
     
        AutoRegister() { &s_factoryregister; }
        static factoryRegister s_factoryregister;
    };
    template<typename typeBase, typename ClassName>
    FactoryRegister<typeBase,ClassName> AutoRegister<typeBase,ClassName>::s_factoryregister;
     
    #endif // FACTORYREGISTER_H
    abstractobject.h:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    #ifndef ABSTRACTOBJECT_H
    #define ABSTRACTOBJECT_H
     
    #include "factoryregister.h"
     
    class AbstractObject
    {
    public:
        typedef QSharedDataPointer<ObjectFactory<QString,AbstractObject> > factory;
     
        AbstractObject() {}
        virtual ~AbstractObject() {}
     
        static factory getFactory() {
            return ObjectFactory<QString,AbstractObject>::getInstance();
        }
     
    #define REGISTER_OBJECT(ClassName) public AutoRegister<AbstractObject,ClassName>
    };
     
    #endif // ABSTRACTOBJECT_H
    La classe cliente TestObject.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #ifndef TESTOBJECT_H
    #define TESTOBJECT_H
     
    #include "abstractobject.h"
     
    class TestObject : REGISTER_OBJECT(TestObject)
    {
    public:
        TestObject();
        ~TestObject() {}
     
        void affiche(QString testString) {qDebug(testString.toAscii());}
    };
     
     
    #endif // TESTOBJECT_H
    et le main.cpp qui affiche bien "Hello"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    #include <QtCore/QCoreApplication>
    #include "testobject.h"
     
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
     
        AbstractObject::factory fac = AbstractObject::getFactory();
        TestObject* testObject = (TestObject*)fac->create("TestObject");
     
        testObject->affiche("Hello");
     
        return a.exec();
    }
    L'enregistrement se fait donc simplement à l'aide d'une macro qui fait hérité la classe de AutoRegister qui hérite elle-même d'AbstractObject, ce qui permet de manipuler les objets par polymorphisme.

    Je met le post sur résolu, mais si vous avez des commentaires ou des améliorations à proposer, n'hésitez pas.

  18. #18
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 751
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 751
    Points : 10 669
    Points
    10 669
    Billets dans le blog
    3
    Par défaut
    Salut,

    perso je m'y perds un peu, alors peut-être que j'ai raté un truc, mais dans ce code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        template<typename T_OBJECT>
        static T_BASE* derivedClassCreationFunc()
        {
            T_OBJECT* t = new T_OBJECT;
            T_BASE* c = dynamic_cast<T_BASE*>(t);
            if (!c) delete t;
            return c;
        }
    pourquoi ne pas utiliser un static_cast ? S'il échoue, tu auras une erreur dès la compilation au lieu d'une erreur au runtime via le dynamic_cast ?

    Et que penses-tu d'une version template de create pour permettre ce genre d'écriture :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TestObject* testObject = fac->create<TestObject>("TestObject");
    peut-être même qu'avec ton système de macro _TYPETOSTRING_ on pourrait directement écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TestObject* testObject = fac->create<TestObject>();
    ?

  19. #19
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Remarques en vrac :
    ObjectFactory :
    => la classe FactoryRegister a 2 responsabilités : singleton et fabrique. Il faudrait les dissocier (SRP) en 2 classes différentes.

    => les autres variables membres ne devraient pas être statiques.

    => idem pour les fonctions

    => le double check tel que tu l'implémente ne fonctionne pas en C++03

    => Plutôt qu'un pointeur de fonction, un std::function offrirait plus de possibilité pour la création des objets

    => Il n'y a pas besoin de cast lorsqu'on convertit un type depuis une classe dérivée vers une classe de base. C'est le principe même de l'héritage que de pouvoir substituer une instance de la classe dérivée à la classe de base.

    AutoRegister
    => je ne vois pas du tout l'intérêt de l'héritage. Ta macro peut très bien être utilisée pour une déclaration d'une variable privée statique d'auto enregistrement. Cette surcouche d'héritage me semble très fragile

    Sinon, c'est trop dépendant de Qt à mon goût. La STL contient à peu près tout ce dont tu as besoin si je ne m'abuse.

  20. #20
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    29
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 29
    Points : 17
    Points
    17
    Par défaut
    Salut,

    Citation Envoyé par Aurelien.Regat-Barrel Voir le message
    Salut,

    perso je m'y perds un peu, alors peut-être que j'ai raté un truc, mais dans ce code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        template<typename T_OBJECT>
        static T_BASE* derivedClassCreationFunc()
        {
            T_OBJECT* t = new T_OBJECT;
            T_BASE* c = dynamic_cast<T_BASE*>(t);
            if (!c) delete t;
            return c;
        }
    pourquoi ne pas utiliser un static_cast ? S'il échoue, tu auras une erreur dès la compilation au lieu d'une erreur au runtime via le dynamic_cast ?
    C'est vrai. Ce sera également plus rapide.


    Citation Envoyé par Aurelien.Regat-Barrel Voir le message
    Et que penses-tu d'une version template de create pour permettre ce genre d'écriture :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TestObject* testObject = fac->create<TestObject>("TestObject");
    peut-être même qu'avec ton système de macro _TYPETOSTRING_ on pourrait directement écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TestObject* testObject = fac->create<TestObject>();
    ?
    Ça pourrait être pas mal en effet. Je testerai ça dans le week-end.

    Merci pour ces suggestions

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Enregistrer des objets en XML : Sérialisation JavaBeans
    Par julien_chable dans le forum Codes sources à télécharger
    Réponses: 0
    Dernier message: 08/03/2011, 18h53
  2. Enregistrement des objets dans la RAM
    Par asma07 dans le forum C#
    Réponses: 7
    Dernier message: 18/10/2010, 17h23
  3. Réponses: 3
    Dernier message: 31/03/2008, 15h13
  4. Réponses: 3
    Dernier message: 12/09/2007, 10h53
  5. Réponses: 2
    Dernier message: 08/12/2006, 01h20

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo