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 :

[Templates] Remplir une structure


Sujet :

C++

  1. #1
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut [Templates] Remplir une structure
    Salut,

    Désolé pour le titre, c'est pas forcément super clair. Je vais donc tâcher d'être le plus précis possible dans la description du problème.

    J'ai de diverses structures telles que celle-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct CardEntry
    {
        uint ID;                        // 0
        uint CardID;                    // 1
        uint GroupID;                   // 2
        uint ExtensionID;               // 3
        QString Name;                   // 4
        QString Description;            // 5
        QString Reference;              // 6
        QString Code;                   // 7
        uint TypeID;                    // 8
    };
    Et mon objectif est de créer une classe template pour créer une QList<T *> (on considèrera que c'est une std::list<T *>, le problème étant un problème de C++ et non de l'utilisation de Qt) de ces structures, avec T = la strucutre.

    Ainsi, j'ai créé une classe template nommée DBStore avec une fonction Load dont le but est de générer cette liste.

    Le problème est de pouvoir remplir les structures template de la liste, dont le compilateur n'a aucune info.

    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
    template<typename T>
    class DBStore
    {
    public:
        DBStore() : _fieldNum(0) {}
        void Load(QSqlDatabase *db, /*...*/, const QString structInfo)
        {
            // ...
     
                // Création d'un T* à remplir puis à mettre dans la liste
                T *data = new T; 
     
                // Avec _fieldNum, le nombre de champs de la structure
                for (uint i = 0; i < _fieldNum; i++)
                {
                    if (structInfo.at(i) == "i") // i pour uint, on connait donc le type présumé du champ
                        data[i] = /*valeur en uint*/; // Erreur
                    else if (structInfo.at(i) == "s") // s pour string, on connait donc le type présumé du champ
                        data[i] = /*valeur sous forme de string*/; // Erreur
                }
     
    // ...
    L'erreur de compilation vient logiquement du fait que data soit un type indéfini, donc logiquement, on ne peut pas faire data[i] = quelque chose. Bref, je ne vois pas du tout comment faire pour assigner une valeur à chaque champ de mes structures.

    Pour résumer, ma question est la suivante :

    Comment faire pour faire data[i] = quelque chose, avec data, un pointeur d'une classe template, en sachant uniquement le type de la valeur que l'on doit y mettre ?
    Merci d'avance et désolé pour la clarté, c'est pas évident à exprimer,
    Amnell.
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Je pense que tu fais quelques erreur sur le manière de t'y prendre :
    • Pourquoi manipuler des pointeurs sur T ? Tu a un type POD, qui ne bénéficie pas du gestionnaire de mémoire de Qt, utiliser des pointeur (nus qui plus est) c'est se tirer une balle dans le pied
    • -ton utilisation de l'operateur[] me parait très bizarre... Vu qu'elle s'applique à un pointeur ton programme d'acceder à des addresse situé à data+i*sizeof(T) et vu que tu n'a alloué que l'espace [data, data+sizeof(T)[, dès que i sera superieur à 0 ton programme plantera. Je comprend ce que tu veux faire MAIS ça ne se fait pas comme çe ( on est dans le forum C++, pas Javascript )

    Maintenant comment résoudre le problème ? Tu as le choix : la manière statique ou dynamie. Vu que ton programme ne semble pas vraiment se soucier des types(au moment de la compilation s'entend) je pense que tu preferera la manière dynamique. L'idée est de refaire une sorte de boost::any ou QVariant adapté c'est à dire utiliser le type erasure.
    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
    66
    67
    68
    69
    70
    71
     
    // D'abord on definit la classe de base
    // avec un operateur= pour les uint et un pour les string
    // qui délèguent le travail à des fonctions virtuelles pures privées
    // je ne présente que pour les uint, faire la même chose pour les string
    class Base
    {
    public:
        Base& operator=(uint i)
        {
             affect_uint(i);
             return *this;
        }
    private:
        virtual void affect_uint(uint i) = 0;
        virtual void affect_string(string s) = 0;
    };
     
    // on dérive pour chaque type ( (*) )
     
    template<class T>
    class SpecialisationUint : public Base
    {
    private:
        typedef int (T::*field_type);
        field_type field_; // mettre le constructeur qui va bien
        T& instance_;
    private:
        virtual void affect_uint(uint i)
        {
              instance_->*field_ = i;
        }
        virtual void affect_string(string s)
        {
              // gère l'erreur (exception, message à la console, au log ...)
        }
    };
     
    // et rajouter une méthode getField() dans la structure :
    struct MaStruct
    {
        uint field1;
        uint field2;
        string field3;
     
        Base* getField(int index)
        {
            if(index == 1)
            {
                   return new SpecialisationUint<MaStruct>(&field1, *this);
            }
    //... et ainsi de suite
           else if(index == 3)
           {
                   return new SpecialisationString<MaStruct>(&field3, *this);
           }
    //...
        }
    };
     
    //Dans ta fonction load :
    T t;
    //..
    if(/* si c'est un int */ )
    {
        *(t.getField(indexField)) = /* valeur uint */;
    }
    else if(/* si c'est un string */ )
    {
        *(t.getField(indexField)) = /* valeur string */;
    }
    Attention le code ci dessus a une grosse fuite de mémoire au niveau de getField. Vu qu'il y a un template tu ne peut pas utilise le gestionnaire de mémoire style Qt ( de toutes façon ta structure ne s'y prêtait pas) il faut donc utiliser un truc genre unique_ptr (PAS de shared_ptr, on ne degomme pas une mouche avec un bazooka)

  3. #3
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    boost::any en dynamique, boost::fusion en statique avec les FUSION_ADAPT_STRUCT

  4. #4
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Citation Envoyé par Joe Dralliam Voir le message
    -ton utilisation de l'operateur[] me parait très bizarre... Vu qu'elle s'applique à un pointeur ton programme d'acceder à des addresse situé à data+i*sizeof(T) et vu que tu n'a alloué que l'espace [data, data+sizeof(T)[, dès que i sera superieur à 0 ton programme plantera. Je comprend ce que tu veux faire MAIS ça ne se fait pas comme çe ( on est dans le forum C++, pas Javascript )
    Je me doutais bien que quelque chose clochait avec cet opérateur. Ça m'apprendra à coder à 4h du matin.

    Citation Envoyé par Joe Dralliam Voir le message
    Maintenant comment résoudre le problème ? Tu as le choix : la manière statique ou dynamie. Vu que ton programme ne semble pas vraiment se soucier des types(au moment de la compilation s'entend) je pense que tu preferera la manière dynamique. L'idée est de refaire une sorte de boost::any ou QVariant adapté c'est à dire utiliser le type erasure.
    Nickel, exactement les infos dont j'avais besoin, merci bien. Je passe le topic en résolu.
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  5. #5
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bon, finalement, j'ai pas tout à fait suivi l'exemple donné, j'ai fait la sauce à ma manière en m'en inspirant tout en utilisant QVariant plutôt que de réinventer la roue. Cela fonctionne, certes, mais je voudrais savoir si c'est syntaxiquement correct selon vous.

    La structure :

    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
    struct CardEntry
    {
        uint ID;                        // 0
        uint CardID;                    // 1
        uint GroupID;                   // 2
        uint ExtensionID;               // 3
        QString Name;                   // 4
        // Etc...
     
        void setData(int index, QVariant value)
        {
            switch (index)
            {
                case 0:
                case 1:
                case 2:
                // Etc... (tous les uint)
                    if (uint *data = dataInt(index))
                        *data = value.toUInt();
                    break;
                case 4:
                // Etc... (tous les string)
                    if (QString *data = dataString(index))
                        *data = value.toString();
                    break;
            }
        }
     
    private:
        uint* dataInt(uint i)
        {
            switch (i)
            {
                case 0: return &ID;
                case 1: return &CardID;
                case 2: return &GroupID;
                // Etc...
                default: return NULL;
            }
        }
        QString *dataString(uint i)
        {
            switch (i)
            {
                case 4: return &Name;
                case 5: return &Description;
                // Etc...
                default: return NULL;
            }
        }
    };
    Et l'affectation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
            QSqlQuery query = db->exec("SELECT * FROM " + table);
     
            while (query.next())
            {
                T *data = new T;
                for (uint i = 0; i < _fieldNum; i++)
                    data->setData(i, query.value(i).toUInt());
                _data.append(data);
            }
    Théoriquement, le problème de la fuite de mémoire est évité.

    Merci encore,
    Amnell.

    EDIT : Il va peut-être s'avérer plus intéressant de faire une classe de base pour les structures de manière à éviter d'avoir à faire un énorme setData. Je propose ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class BaseEntry
    {
    public:
        void setData(int index, QVariant value)
        {
            if (uint *data = dataInt(index))
                *data = value.toUInt();
            else if (QString *data = dataString(index))
                *data = value.toString();
        }
    private:
        virtual uint* dataInt(uint i) = 0;
        virtual QString *dataString(uint i) = 0;
    };
    Ce qui donnera une structure comme ceci :

    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
    struct CardEntry : public BaseEntry
    {
        uint ID;                        // 0
        uint CardID;                    // 1
        uint GroupID;                   // 2
        uint ExtensionID;               // 3
        QString Name;                   // 4
        // Etc...
     
    private:
        uint* dataInt(uint i)
        {
            switch (i)
            {
                case 0: return &ID;
                case 1: return &CardID;
                case 2: return &GroupID;
                // Etc...
                default: return NULL;
            }
        }
        QString *dataString(uint i)
        {
            switch (i)
            {
                case 4: return &Name;
                case 5: return &Description;
                // Etc...
                default: return NULL;
            }
        }
    };
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  6. #6
    Invité
    Invité(e)
    Par défaut
    Vu que ta structure n'a absolument rien de virtuelle et que l'implémentation de setData() est un "détails", je pencherais plutot pour un Curiously Recurring Template Pattern + héritage privée + using :
    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
     
    template<class Derivee>
    class Base
    {
         Derivee& d_;
    public:
         Base(Derivee d):d_(d)
         {}
     
         void setData(int index, QVariant value)
        {
            if (uint *data = d_.dataInt(index))
                *data = value.toUInt();
            else if (QString *data = d_.dataString(index))
                *data = value.toString();
        }
    };
     
    struct MaStruct : private Base<MaStruct>
    {
         friend Base<MaStruct>;
     
         MaStruct():Base<MaStruct>(*this)
         {}
     
         using Base<MaStruct>::setData;
    private:
         //dataString & dataUint
    };

  7. #7
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonsoir,

    Désolé de la réponse tardive. Bref, je reviens vers vous avec une nouvelle solution, et je m'attends déjà au pire, ayant rarement eu à coder un truc de la sorte.

    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
    66
    67
    struct CardEntry
    {
        int ID;                         // 0
        int CardID;                     // 1
        int GroupID;                    // 2
        int ExtensionID;                // 3
        const char* Name;               // 4
        // ...
    };
     
    enum
    {
        FORMAT_STRING   = 's',
        FORMAT_INT      = 'i',
    };
     
    int GetStructSize(const char * format)
    {
        int size = 0;
        for (int i = 0; format[i]; ++i)
        {
            switch (format[i])
            {
                case FORMAT_INT:
                    size += sizeof(int*);
                    break;
                case FORMAT_STRING:
                    size += sizeof(char*);
                    break;
            }
        }
        return size;
    }
     
    MainWindow::MainWindow()
    {
        const int ENTRY_COUNT = 10;
        const int FIELD_COUNT = 15;
        const char *format = "iiiisssiiisiiii";
        const char *text = "Bonsoir";
     
        CardEntry *entry[ENTRY_COUNT];
     
        char* dataTable = new char[ENTRY_COUNT * GetStructSize(format)];
     
        int offset = 0;
     
        for (int i = 0; i < ENTRY_COUNT; ++i)
        {
            entry[i] = (CardEntry*)&dataTable[offset];
     
            for (int y = 0; y < FIELD_COUNT; ++y)
            {
                switch (format[y])
                {
                case FORMAT_INT:
                    *((int*)(&dataTable[offset])) = 10;
                    offset += sizeof(int*);
                    break;
                case FORMAT_STRING:
                    *((char**)(&dataTable[offset])) = (char*)text;
                    offset += sizeof(char*);
                    break;
                }
            }
        }
    }
    (À noter que c'est un simple code de test réalisé en une soirée - je m'excuse pour les casts et le coding style à la C).

    L'avantage de ce code est qu'on n'a pas réellement besoin de connaître le nom des variables de la structure, seulement leur type via un const char* format. La différence majeure que je vois par rapport aux solutions que vous m'avez fournies est qu'on n'a pas besoin de blinder les structures avec un ou plusieurs accesseurs.

    Qu'en pensez-vous ? Est-ce correct ? (S'il y a des choses franchement frappantes, n'hésitez pas à les souligner, ça ne pourra qu'être bénéfique.)

    Merci encore,
    Amnell.
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Remplir une structure "lecture fichier"
    Par SYNAN dans le forum C
    Réponses: 1
    Dernier message: 19/06/2013, 15h07
  2. Réponses: 4
    Dernier message: 06/05/2010, 19h24
  3. impossible de remplir une structure à partir d'un fichier binaire
    Par étoile de mer dans le forum Débuter
    Réponses: 3
    Dernier message: 21/12/2009, 12h28
  4. remplir une structure de donnée récursive
    Par robert_trudel dans le forum Débuter avec Java
    Réponses: 3
    Dernier message: 23/05/2008, 16h32
  5. [Initialisation] Remplir une structure une fois
    Par Kimael dans le forum Langage
    Réponses: 14
    Dernier message: 08/06/2004, 15h33

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