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 :

pointeur sur donnée membre


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Février 2006
    Messages
    153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 153
    Par défaut pointeur sur donnée membre
    Bonjour tout le monde

    J'espere que le pere noël a été sympa avec vous

    Je developpe une appli qui charge des données typées (depuis une BdD). Je souhaiterai créer un framework qui permet de simplifier au max le travail de l'utilisateur de ce framework.

    J'ai donc commencer par me créer une class Value :
    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
    struct NoCheckValidator
    {
    	template <typename T>
    	static bool Check(const T& /*value*/)
    	{
    		return true;
    	}
    };
     
    template <typename T, typename C, typename V = NoCheckValidator>
    class Value
    {
    public:
    	typedef T value_t;
    	typedef C convert_functor;
    	typedef V validator_t;
     
    private:
    	value_t   m_value;
     
    public:
    	Value(const value_t& v = value_t())
    	: m_value(v)
    	{
    	}
     
    	Value(wxSQLite3ResultSet& res, const wxString& colName, const value_t& defaultValue = value_t())
    	{
    		load(res, colName, defaultValue);
    	}
     
    	virtual ~Value()
    	{
    	}
     
    	const value_t& operator()() const 
    	{
    		return m_value;
    	}
     
    	bool operator()(const value_t& v) 
    	{
    		return set(v);
    	}
     
    	bool load(wxSQLite3ResultSet& res, const wxString& colName, const value_t& defaultValue = value_t())
    	{
    		return set( convert_functor::load(res, colName, defaultValue) );
    	}
     
    protected:
    	bool set(const value_t& v)
    	{
    		if (validator_t::Check(v) == false) {
    			return false;
    		}
    		m_value = v;
    		return true;
    	}
    };
    Je peux donc créer des types de valeur, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct int_converter
    {
    	static
    	int load(wxSQLite3ResultSet& res, const wxString& colName, int defaultValue)
    	{
    		return res.GetInt(colName, defaultValue);
    	}
    };
    typedef Value<int, int_converter>    integer;
    A présent, je peux donc me créer des classes contenant ces Value :
    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
    class Cost : public db::Table
    {
    public:
    	db::real   unit_cost;
    	db::real   quantity;
    	db::text   name;
    	db::date   date;
    	db::ID     id_task;
     
    	static const wxString TABLE_NAME;
     
    public:
    	Cost();
    	Cost(wxSQLite3ResultSet& res);
     
    	double cost() const { return unit_cost() * quantity(); }
     
    	// DB
    	virtual db::update_value_type prepare_insert(wxSQLite3Database& db);
    	virtual db::update_value_type prepare_update(wxSQLite3Database& db);
    };
    Avec ceci, je suis tout de même obligé de charger dans le constructeur qui possede le resultat de la requete, l'ensemble des variables. Ce que j'aimerai, c'est pouvoir créer une variable static, permettant de connaitre les variables et donc de les charger automatiquement dans la classe de base (db::Table dans mon cas). Un truc du genre :

    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
    template <typename T>
    class Table
    {
        static ColumnsContainer m_columns; // oui mais quoi ?
        ...
     
        bool load(...); // il ferait tout
    };
     
    bool Table::load(...)
    {
        BOOST_FOREACH(var, m_columns) {
            var.load(...);
        }
    }
     
    class Cost : public Table<Cost>
    {
        ...
    }
     
    Table<Cost>:: ColumnsContainer Table<Cost>::m_columns = { &Cost::name, ... }; // un truc a base de boost::assign
    // ou mieux, avec un define
    DEFINE_DB_VARS_nn(Cost, name, ...); // avec nn parametres
    Je comprends qu'on ne peut pas créer un pointeur sur une variable membre sans avoir instancié l'objet, mais y a t il une solution à ce que je souhaite ?

  2. #2
    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
    Par défaut
    Salut,
    Je ne vais pas répondre directement à ta question mais sait-on jamais...
    En général, lorsque j'ai des objets complexes, ils ne se construisent pas tout seul. J'utilise ce qui ressemble à un DP builder qui est ami de la classe à construire. Peut être est-ce une solution à ton problème ?

  3. #3
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Si j'ai bien compris, tu cherches à faire un mapping entre la base de données et des classes C++. En C++, les types sont statiques, donc un tel mapping ne peut pas être résolu à l'exécution.

    Du coup, je ne suis pas sûr que l'approche par template soit la plus pertinente...

  4. #4
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Février 2006
    Messages
    153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 153
    Par défaut
    Salut

    Citation Envoyé par 3DArchi Voir le message
    En général, lorsque j'ai des objets complexes, ils ne se construisent pas tout seul. J'utilise ce qui ressemble à un DP builder qui est ami de la classe à construire.
    Je ne connais pas ce principe, et google ne m'a pas fourni de lien avec "DP Builder". Peux tu me donner plus d'infos ?

    En C++, les types sont statiques, donc un tel mapping ne peut pas être résolu à l'exécution.
    Tout a fait, mais ce n'est pas ce que je cherche à faire. Je cherche simplement (?) à simplifier la création d'objet mappant une table, sachant que l'utilisateur doit connaitre les colonnes et leurs type.

    Pour reprendre le code précedent :

    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 <typename T>
    class Table
    {
        static ColumnsContainer m_columns; // oui mais quoi ?
        ...
     
        bool load(...); // il ferait tout
    };
     
    bool Table::load(...)
    {
        BOOST_FOREACH(var, m_columns) {
            var.load(...);
        }
    }
     
    class Cost : public Table<Cost>
    {
    public:
        db::integer  int_value;
     
     
    }
     
    // Definir le lien entre un nom de colonne et une variable
    Table<Cost>:: ColumnsContainer Table<Cost>::m_columns = { (&Cost::name, "name", ... }; // un truc a base de boost::assign
    // ou mieux, avec un define
    // ou name represente le nom de la variable membre et "name" represente le nom de la colonne
    DEFINE_DB_VARS_nn(Cost, (name, "name", ...); // avec nn colonnes
    Peut être n'est ce pas possible...

  5. #5
    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
    Par défaut
    Salut,
    DP Builder.
    Au départ, j'ai eu envie d'appliquer un type erasure sur Value :
    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 value_base
    {
        virtual ~value_base()=default;
    };
     
    template<class T>
    struct value : public value_base
    {
        T value;
    };
     
    template <typename T>
    class Table
    {
        struct ColumnsContainer
        {
            std::pair<std::string,value_base T::*> elt;
        };
     
        static ColumnsContainer m_columns; // oui mais quoi ?
    };
     
     
    class Cost : public Table<Cost>
    {
    public:
        value<int>  int_value;
    };
     
    // Definir le lien entre un nom de colonne et une variable
    template<>
    Table<Cost>::ColumnsContainer Table<Cost>::m_columns = {{"name", &Cost::int_value} }; // un truc a base de boost::assign
    mais ça ne marche pas
    invalid conversion from 'value<int> Cost::*' to 'value_base Cost::*'
    J'ai l'impression que la piste est par là, mais peut être en ne prenant pas en compte un value mais un contructeur de value hors de la classe ?

  6. #6
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Tout a fait, mais ce n'est pas ce que je cherche à faire. Je cherche simplement (?) à simplifier la création d'objet mappant une table, sachant que l'utilisateur doit connaitre les colonnes et leurs type.
    Pour les types des colonnes, je pense à un tuple (il y a ça dans boost). Pour les noms des colonnes, un enum. Pourquoi un enum plutôt qu'une chaîne ? Parce que si, de toute manière, ça doit être connu à la compilation, autant utiliser les possibilités de vérification que nous donne l'utilisation d'un enum.

    Auquel cas, l'utilisateur n'aurait plus qu'à écrire quelque chose du genre :

    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
    class ExampleTableParameters
    {
        typedef tuple<int, double, std::string> ColumnsType;
        enum Column {
            Id = 0,
            Amount = 1,
            Comment = 2
        };
        static Column GetIndex(std::string columnName)
        {
            if(columnName == "Id")
                return Id;
            ...
        }
     
        static std::string GetColumnName(Column idx)
        {
             switch(idx)
             {
                 case Id: return "Id";
                 case Amount : return "Amount";
                 ...
             }
        }
    };
     
    class ExampleTable : GenericTable<ExampleTableParameters>
    { // rien à faire à priori
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<typename Parameters> class GenericTable
    {
    ...
    }
    De ton côté, tu as le plus gros du boulot dans GenericTable :

    - stocker les valeurs en utilisant le tuple (et donc, fournir les diverses conversions pour initialiser correctement chacune des valeurs du tuple avec les valeurs lues depuis la base).
    - fournir une méthode GetValue(Parameters::Column). Je ne crois pas que tu puisses t'en sortir autrement qu'avec un gros switch sur la valeur pour repasser du runtime au compile-time, ce qui induit malheureusement une limite sur le nombre de colonnes (si ça peut te rassurer, il me semble que tu as déjà cette limite à cause de tuple, donc tu ne la rajoutes pas).
    - fournir une méthode (renvoyant un variant) GetValue(std::string const & columnName), qui utilise Parameters::GetColumnIndex et GetValue(Parameters::Column).

    Ensuite, il me semble intéressant de rajouter quelques politiques au niveau du chargement des données, du cache, etc (on ne veut pas nécessairement charger toute la table en mémoire).

  7. #7
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Février 2006
    Messages
    153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 153
    Par défaut
    Salut, et merci pour les pistes que vous proposez

    Citation Envoyé par 3DArchi Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    invalid conversion from 'value<int> Cost::*' to 'value_base Cost::*'
    J'ai l'impression que la piste est par là, mais peut être en ne prenant pas en compte un value mais un contructeur de value hors de la classe ?
    Mince alors, je n'avais même pas testé car pour moi un pointeur sur une variable membre ne signifie rien. Pour une méthode, ca me parait logique : c'est l'@ du début de la méthode, que l'on peut observer sans instancier d'objet. Mais une variable n'a pas d'@ sans instanciation d'un objet ! la notion &Classe::variable ne peut exister.

    Le coup de switch / case sur des enum ne semble pas simplifier le travail de l'utilisateur, qui va devoir faire plus d'un coté pour en faire moins de l'autre...

  8. #8
    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
    Par défaut
    Citation Envoyé par jfouche Voir le message
    Mais une variable n'a pas d'@ sans instanciation d'un objet ! la notion &Classe::variable ne peut exister.
    Tu peux voir ça un peu comme un offset sur l'adresse de l'objet qui sera donné pour utiliser le pointeur. En fait, avec une fonction membre le problème peut se poser : quelle adresse pour une fonction membre virtuelle ? (teasing : la réponse très bientôt).

Discussions similaires

  1. [POO] Pointeur sur fonction membre et héritage
    Par MrDuChnok dans le forum C++
    Réponses: 9
    Dernier message: 20/07/2006, 17h19
  2. Réponses: 5
    Dernier message: 12/06/2006, 14h56
  3. Pointeur sur fonction membre avec parametre
    Par Glosialabolas dans le forum C++
    Réponses: 7
    Dernier message: 06/02/2006, 02h32
  4. Réponses: 10
    Dernier message: 03/02/2005, 13h09
  5. Réponses: 5
    Dernier message: 12/01/2005, 20h58

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