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

 C++ Discussion :

Instancier classes et attributs à partir de leur nom


Sujet :

C++

  1. #1
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 54
    Par défaut Instancier classes et attributs à partir de leur nom
    Bonjour à tous. Bien qu'ayant l'habitude de développer, en particulier de l'objet, je début avec la syntaxe C++ donc je viens vous demander de l'aide pour un projet

    Le but est d'arriver à lire des instances de classes décrites en XML sous la forme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    <classA ID="ID_A">
       <classA.attr1>value1</classA.attr1>
       <classA.attr2>value2</classA.attr2>
    </classA>
     
    <classB ID="ID_B">
       <classB.attr1>value1</classB.attr1>
       <classC.attr1>value3</classC.attr1>
       <classE.attr4>value4</classE.attr4>
       <classG ID = "ID_G" />
    </classB>
     
    ...
    Les classes y sont décrites "à plat". Comme dans mon exemple, si classe B est une classe dérivée de C, et C une classe dérivée de E, les attributs de certaines classes mères sont définis au niveau des classes filles (ces classes sont considérées "abstraites" et sont connues).

    Les "ID" servent de référence au sein du fichier pour décrire les associations entre les classes.

    Par ailleurs, le modèle global de données est bien spécifié (classes, attributs, associations possibles) Ainsi j'ai pour l'instant dans mon modèle de données C++ l'ensemble des classes existantes, avec leurs attributs (et associations). Par exemple donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class classB : public classC{
     
    public :
          int attr1;
     
    }
    L'objectif est donc d'être capable de lire d'énormes (plusieurs dizaines voire centaines de Mo) de ces fichiers XML pour peupler le modèle de données. L'aspect performance est donc à considérer au niveau du parser. (Bien entendu, SAX sera utilisé, mais moins il y a de calculs et surtout de redondance et meilleur ce sera ). Le temps de compilation est lui indifférent.

    Pour faciliter le développement à venir, toutes ces classes héritent directement ou indirectement d'une classe que l'on appellera "Object" (oui je viens du monde Java )

    A noter également qu'aucune vérification du fichier XML n'a besoin d'être effectuée. Ces fichiers sont considérés parfaitement valides et donc les références présentes existent bien, ainsi que l'ensemble des classes et attributs associés ou encore les valeurs littérales indiquées.


    La première étape consiste donc à instancier une classe à partir son nom.
    Une deuxième étape sera de "setter" l'attribut de la classe, ou d'une de ces ancêtres. S'il s'agit d'une association l'ID de la classe associée est indiquée et on peut alors supposer que garder une map des objets crées est judicieux (id -> Object* par exemple). Si l'objet associé n'a pas encore été créé, il faudra le créer sans rien setter : il sera défini plus tard dans le fichier.
    Ainsi la première étape consiste en fait à récupérer l'instance si elle existe, et à la créer sinon.

    Pour réaliser les fonctions adéquates, j'imagine qu'il me faut faire appel aux "templates" et "typename". De plus je compte créer une map initialisée au lancement du programme associant le nom d'une classe à son constructeur pour gérer les instanciations sans avoir à parcourir une bonne centaine de "if" sur le nom lu de classe (dans un souci de performance puisque les fichiers sont assez volumineux ...) et je pensais donc à une classe du genre (pardonnez moi, c'est un brouillon dans lequel je zappe les includes et je mets tout en public dans un même fichier source ...) :

    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
     
    class Model{
    public :
       map<string, Object*(*)(string)> name_ctor  //map (nom -> constructeur)
       map<string, Object*> name_object  // map (id -> instance)
     
       Object currentObject // object que l'on est en train de setter
     
     
       Model(){
    	  // créer la map name_ctor : comment faire ?
    	  // Je me doute qu'il faut écrire X fois la "même" ligne, mais c'est la partie "valeur" de la map qui me bloque, celle qui pointe vers une fonction
       }
     
       template<typename T> createInstance(string name, string id){
          // comment l'écrire ? Il faut vérifier l'existence et effectivement créer si besoin
       }
     
       // Il manque des fonctions pour que tout fonctionne bien ... Help :p
     
     
       void readAttribute(string motherClass, string attribute, string value){
     
     
     
          // Chaque "Object" implémente sa fonction "read(string, string, string)"
    	  // Utiliser le mot-clé "virtual" dans les classes mères "abstraites" ??
          currentObject.read(string motherClass, string attribute, string value);
       }
    }
     
    class classB : public classC{
     
    public :
          int attr1;
     
     
    	  void read(string motherClass, string attribute, string value){
    	     //(il n'y a pas énormément d'attribut, donc un "if then else" est envisageable
    		 // une map serait mieux, mais je peine un peu ...
    	     if(strcmp(attribute, "attr1")){
    		   // setter
    		 }
    		 else if ...
     
    		 ...
     
    		 else{
    		    // classC fera la même chose ...
    			// Gérer le "motherClass" serait plus judicieux mais je peine encore ...
    		    classC::read(motherClass, attribute, value);
    		 }
    	  }
     
    }


    Voilà en gros pour mon code à trous donc si quelqu'un peut m'aider à déblayer tout ça ... J'ai un peu de mal à voir comment faire pour obtenir un code propre et optimisé en fonction des possibilités qu'offre C++.

    Merci

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    Le C++ ne permet pas nativement de faire la correllation entre une chaine de caractères du type "NomDeClass" et la classe correspondante.

    Ce qu'il te faudrait donc peut être envisager est, à mon sens, fort proche du pattern factory.

    L'idée générale consiste, pour toute hiérarchie proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class A
    {
    };
    class B : public A
    {
    };
    class C : public A
    {
    };
    à disposer d'un tableau associatif clé / valeur, dont la clé sera une chaine de caractères (correspondant à celle de ton fichier XML) et dont la valeur est... un pointeur vers une instance du type particulier.

    Dans l'interface de A, il "suffit" de s'assurer d'une méthode virtuelle de clonage (dont le nom sera souvent... clone()), qui permettra de renvoyer un nouvel objet du type adéquat, sous une forme proche de
    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
    class A
    {
        public:
            /* nécessaire pour assurer le polymorphisme */
            virtual ~A(){}
            virtual A* clone() const{return new A;}
    }
    class B
    {
        public:
           virtual B* clone() const{return new B;}
    };
    class C
    {
        public:
            virtual C clone() const{return new C;}
    };
    Tu crées donc, en supplément, une classe (qui peut être un singleton) qui contient l'association entre le nom de la classe (sous forme de chaine de caractères) et... le type correspondant, sous une forme proche de
    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
    class Factory
    {
            /* deux typedef pour faciliter l'écriture à usage interne 
             * uniquement ;)
             */
            typedef std:: map<std::string, A*> map;
            typedef map::const_iterator const_iterator;
        public:
            Factory & instance()
            {
                static Factory f;
                return f
            }
            A* create(std::string const& str)
            {
                 /* cherchons la chaine correspondante */
                 const_iterator it=items_.find(str):
                 /* nous renvoyons NULL si la chaine n'existe pas */
                 if(it==items_.end())
                     return NULL; /* (pourrait en fait lancer une exception ;) */
                 /*invoquons le comportement de clonage */
                 return (*it).second->clone();
            }
        private:
            /* nous ne pouvons pas accéder directement au constructeur
             * parce que la classe est un signleton
             */
            Factory()
            {
                /* enregistrons les différents types à créer */
                items_.insert(std::make_pair("ClassA", new A));
                items_.insert(std::make_pair("ClassB", new B));
                items_.insert(std::make_pair("ClassC", new C));
                /*  ...*/
            }
            ~Factory()
            {
                /* libérons correctement la mémoire de nos modèles d'objets  */
                for(const_iterator it=items_.begin(); it!=items_.end(); ++it)
                    delete (*it).second;
            }
            /* la fabrique ne peut être ni copiée ni assignée...
             * nous déclarons, sans les définir, les constructeur par copie
             * et opérateur d'affectation
             */
            Factory(Factory const &);
            Factory & operator=(Factory const &);
            map items_;
    }
    L'utilisation "générale" de cette classe se faisant sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::string str;
    /* lecture dans le fichier de la chaine identifiant le type à créer */
    A* ptr=Factory::instance().create(str);
    /* utilisation de ptr  */
    Tu peux également te tourner vers la bibliothèque boost::serialize qui permet, justement, de sérialiser et de désérialiser des données, et qui gère le format XML
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. Réponses: 10
    Dernier message: 25/08/2010, 09h09
  2. Réponses: 4
    Dernier message: 05/10/2009, 15h19
  3. Type de classe non enregistrée à partir de son nom
    Par Grosbenji dans le forum C++Builder
    Réponses: 2
    Dernier message: 14/05/2009, 08h51
  4. [Débutant] Création générique de matrices à partir de leur nom
    Par riri1483 dans le forum MATLAB
    Réponses: 2
    Dernier message: 10/06/2008, 18h07
  5. Réponses: 7
    Dernier message: 10/11/2005, 10h09

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