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 :

Pointeurs de fonction membre à paramètres variables!


Sujet :

Langage C++

  1. #1
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut Pointeurs de fonction membre à paramètres variables!
    Salut tout le monde.

    Je tente une implémentation que les interface ne m'offriraient pas comme je veux.

    En effet j'ai une classe abstraite d'objets que je j'appelle Les classes concrètes dérivées de cette dernière implémentent des fonctions qui définissent leur comportement, comportement variant en fonction du type concret de l'objet.

    Le mécanisme permettant à un objet d'exposer ses méthodes, est encapsulé dans une classe que j'appelle , possédant un nom, une description,,,,, et tout qui permet à un humain de comprendre son rôle.

    Ainsi une action possède un pointeur sur fonction membre d'objet en attribut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int (AObject::* _action)(AObject *);
    et une objet possède une liste d'action.

    Comme vous le remarquez, dans la signature de mon pointeur sur fonction membre, je passe un objet abstrait en paramètre, ce qui limite fortement les possibilités d'utilisation, voire même est inutile.

    Ce que j'aimerais c'est pouvoir déclarer l'attribut avec des paramètres variables, de sorte qu'on puisse lui assigné un pointeur sur fonction membre d'objet avec n'importe quel paramètre.
    Puisqu'au fond cet attribut ne sert qu'à stocker une fonction de la classe Pour info je développe sous Qt, et je ne vois pas trop comment le faire avec des QVariant.

    Je n'y arrive pas non plus avec les templates variadiques.

    J'aimerais donc savoir si quelqu'un ici appréhende un peu le truc, et aurait une solution à me proposer.

    Merci pour votre aide.

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Si j'ai bien compris ta problématique, regarde du côté de function+bind (selon l'âge de compilateur, c'est soit dans std::, soit dans std::tr1::, soit il te faudra utiliser les versions dans boost).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    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,

    D'abord, je te conseillerais d'éviter les pointeurs de fonctions au profit d'objet plus souple comme std::function.

    Tout dépend comment se fait l'appel.

    * Si la variété de paramètres se fait au moment de la construction de l'Action, alors cela peut se faire avec une lambda capturant un contexte :
    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    #include <iostream>
    #include <string>
    #include <functional>
     
    class AObject
    {
    public:
       virtual ~AObject()=0;
    };
    AObject::~AObject(){}
     
    class AConcrete : public AObject
    {
    public:
       void do_something(std::string a, int b) const
       {
          std::cout<<"a = "<<a<<"\n";
          std::cout<<"b = "<<b<<"\n";
       }
     
       virtual ~AConcrete(){}
    };
     
    class Action
    {
    public:
       Action(std::string const& name_, std::string const& description_)
          :name(name_),description(description_)
       {
       }
       void operator()(AObject const &o) const
       {
          action(o);
       }
     
       virtual ~Action(){}
     
    private:
       virtual void action(AObject const&)const = 0; 
       std::string name;
       std::string description;
     
    };
     
    template<class Object_t>
    class ActionConcrete : public Action
    {
    public:
       template<class Callable>
       ActionConcrete(std::string const& name_, std::string const & description_,Callable action_)
          :Action(name_,description_),f_action(action_)
       {
       }
     
    private:
       virtual void action(AObject const& o_)const
       {
          f_action(static_cast<Object_t const&>(o_));
       }
     
       std::function<void (Object_t const&)> f_action; 
    };
     
     
    void invoke(AObject const& obj,Action const& act)
    {
       act(obj);
    }
     
    int main()
    {
       const int i = 0;
       const std::string s = "Hello";
       ActionConcrete<AConcrete> do_something("do_something", "call do_something", [i,s](AConcrete const &o_)
       {
          o_.do_something(s,i);
       }
       );
     
       AConcrete concrete;
     
       invoke(concrete,do_something);
     
     
       return 0;
    }
    * Avec bind :
    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    #include <iostream>
    #include <string>
    #include <functional>
     
    class AObject
    {
    public:
       virtual ~AObject()=0;
    };
    AObject::~AObject(){}
     
    class AConcrete : public AObject
    {
    public:
       void do_something(std::string a, int b) const
       {
          std::cout<<"a = "<<a<<"\n";
          std::cout<<"b = "<<b<<"\n";
       }
     
       virtual ~AConcrete(){}
    };
     
    class Action
    {
    public:
       Action(std::string const& name_, std::string const& description_)
          :name(name_),description(description_)
       {
       }
       void operator()(AObject const &o) const
       {
          action(o);
       }
     
       virtual ~Action(){}
     
    private:
       virtual void action(AObject const&)const = 0; 
       std::string name;
       std::string description;
     
    };
     
    template<class Object_t>
    class ActionConcrete : public Action
    {
    public:
       template<class Callable>
       ActionConcrete(std::string const& name_, std::string const & description_,Callable action_)
          :Action(name_,description_),f_action(action_)
       {
       }
     
    private:
       virtual void action(AObject const& o_)const
       {
          f_action(static_cast<Object_t const&>(o_));
       }
     
       std::function<void (Object_t const&)> f_action; 
    };
     
     
    void invoke(AObject const& obj,Action const& act)
    {
       act(obj);
    }
     
    int main()
    {
       const int i = 0;
       const std::string s = "Hello";
       ActionConcrete<AConcrete> do_something("do_something", "call do_something", std::bind(&AConcrete::do_something,std::placeholders::_1,s,i));
     
       AConcrete concrete;
     
       invoke(concrete,do_something);
     
     
       return 0;
    }
    * Si la variété des paramètres n'est connu qu'à l'appel, tu peux passer par boost.fusion :
    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
    72
    #include <iostream>
    #include <string>
    #include <functional>
    #include <boost\fusion\container\vector.hpp>
    #include <boost\fusion\functional\invocation.hpp>
     
    class AObject
    {
    public:
       virtual ~AObject()=0;
    };
    AObject::~AObject(){}
     
    class AConcrete : public AObject
    {
    public:
       void do_something(std::string a, int b) const
       {
          std::cout<<"a = "<<a<<"\n";
          std::cout<<"b = "<<b<<"\n";
       }
     
       virtual ~AConcrete(){}
    };
     
     
    template<class Callable>
    class Action
    {
    public:
       Action(std::string const& name_, std::string const& description_,Callable callable_)
          :name(name_),description(description_),callable(callable_)
       {
       }
       template<class Sequence>
       void operator()(Sequence param_) const
       {
          boost::fusion::invoke_procedure(callable,param_);
       }
    private:
       std::string name;
       std::string description;
       Callable callable;
     
    };
    template<class Callable>
    Action<Callable> make_Action(std::string const& name_, std::string const& description_,Callable callable_)
    {
       return Action<Callable>(name_,description_,callable_);
    }
     
    template<class action_t>
    void invoke(AConcrete const& obj,action_t act)
    {
       const int i = 0;
       const std::string s = "Hello";
       typedef boost::fusion::vector<AConcrete const&,std::string,int> param_t;
       param_t param(obj,s,i);
       act(param);
    }
     
    int main()
    {
       auto do_something = make_Action("do_something", "call do_something", &AConcrete::do_something);
     
       AConcrete concrete;
     
       invoke(concrete,do_something);
     
     
       return 0;
    }

  4. #4
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut
    Ok Merci!

    J'essaye de tester tout ça voir.

    Mais j'aimerais faire une petite parenthèse sur un truc que je viens de voir dans ton code :

    Il y a une vraie raison de mettre un destructeur virtuel en pure? Moi je n'ai pas réussi à me faire une raison réelle.

    Ou alors le but est juste de forcer sa ré-implémentation dans les classes derivéesé

    Je le mets généralement en virtuel, et c'est tout.

    Merci

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par sympaval Voir le message
    Il y a une vraie raison de mettre un destructeur virtuel en pure? Moi je n'ai pas réussi à me faire une raison réelle.
    Surtout, ça marque de façon évidente que la classe est abstraite.
    Tu as une classe que tu veux abstraite mais dont aucune fonction ne se prête vraiment à être virtuelle pure. A ce moment, le destructeur, puisqu'il doit être virtuel dans le cadre d'un héritage, peut être virtuel pur pour donner la caractéristique abstraite à la classe. A une petite différence près avec une autre fonction, c'est que le destructeur n'étant pas héritée : tu n'es donc pas obligé de le redéfinir dans les classes dérivées et une classe dérivée ne définissant pas de destructeur perd le caractère abstrait :
    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
    class Base
    {
      public:
      virtual ~Base()=0;
    };
    Base::~Base()=default;
     
    class Derivee : public Base
    {
     
    };
     
    int main()
    {
        Derivee d; // OK, Derivee n'est pas une classe abstraite !
        Base b; // 'Base' : cannot instantiate abstract class
     
        return 0;
    }

  6. #6
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut
    Ok!

    C'est vrai qu'à mon sens je pensais qu'il fallait au moins une méthode pure dans une classe pour la rendre abstraite, pas spécialement son destructeur. J'avais pas encore testé sous cette angle. Je vais essayer de creuser.

    Maintenant pour revenir au sujet même qui nous interpellait, j'ai bien compris les solutions qui ont été proposées. Je m'apprête à tester et faire un choix d'intégration à ma solution en développement.

    Par contre j'aimerais bien dans la mesure du possible implémenter les pattern ci dessus via QT, pour n'avoir à rester que dans les outils fourni par le framework.

    Et une question toujours dans le but de comprendre la pertinence des choix en C++ : pourquoi penses - tu que que je dois abandonner les pointeurs sur fonction dans ce cadre? Juste parce que pour le coup ça limite mes implémentations tandis que les std::function les facilite?

    Merci pour votre collaboration autour du sujet.

  7. #7
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut
    Je constate aussi que le compilateur C++ standard ne reconnaît pas les comme faisant partie de l'espace de nommage std.

    Ça relève exclusivement de C++11 ça.

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Je ne connais pas Qt donc je ne sais pas s'il y a d'équivalent.

    Citation Envoyé par sympaval Voir le message
    Et une question toujours dans le but de comprendre la pertinence des choix en C++ : pourquoi penses - tu que que je dois abandonner les pointeurs sur fonction dans ce cadre? Juste parce que pour le coup ça limite mes implémentations tandis que les std::function les facilite?
    A mon sens cela représente mieux le concept que tu veux utiliser : une fonction avec une certaine signature. std::function te permet ainsi (à signature égale bien sûr) de contenir un pointeur sur fonction membre, une fonction libre, un foncteur, une lambda, le résultat d'un bind, etc... En ce sens, ça facilite l'évolution et la maintenance. En plus, avec un pointeur non initialisé, c'est plantage. Avec un std/tr1/boost::function non initialisé, c'est une exception.

    Citation Envoyé par sympaval Voir le message
    Je constate aussi que le compilateur C++ standard ne reconnaît pas les comme faisant partie de l'espace de nommage std.

    Ça relève exclusivement de C++11 ça.
    C'est du C++11 mais tu peux le trouver dans tr1 avec un compilateur l'implémentant.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <tr1/functional>
    #include <iostream>
    void foo()
    {
        std::cout<<"foo\n";
    }
    int main()
    {
        std::tr1::function<void ()> f = foo;
        f();
        return 0;
    }
    Sinon, c'est Boost
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <boost\function.hpp>
    #include <iostream>
    void foo()
    {
        std::cout<<"foo\n";
    }
    int main()
    {
        boost::function<void ()> f = foo;
        f();
        return 0;
    }

  9. #9
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut
    Merci encore pour tes réponses.

    Tu as entièrement raison, et j'ai pu recoder les function de la std.

    Le truc c'est que je veux pas compiler avec des lib en plus de Qt. Là c'est bon pour les std::function, ça passe super bien, et c'est de la méta prog, vraiment hyper pratique.

    Par contre je ne vois pas un autre moyen de stocker de manière anonyme la signature de mes fonctions que par des lambda comme tu l'as fait dans ton exemple.

    Et du coup je me retrouve obligé d'utiliser C++11 si je veux utiliser les lambda, je n'ai par contre pas l'intention de me faire mes lambda à moi.

    Je vois pas trop comment faire à ce niveau en restant dans le c++ standard + QT.

    Merci encore.

  10. #10
    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,
    Citation Envoyé par sympaval Voir le message
    Par contre je ne vois pas un autre moyen de stocker de manière anonyme la signature de mes fonctions que par des lambda comme tu l'as fait dans ton exemple.
    Regardes du côté de std/boot/tr1::bind.

    Si vraiment tu ne t'en sorts pas avec ça, alors tu peux toujours coder un foncteur à la main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct mon_foncteur_adhoc
    {
    public:
       void operator()() [const]
       {
       }
    private:
     TYPE mon_etat;
    };

  11. #11
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut
    Merci AdArchi!

    En fait c'est pas que je m'en sort pas avec les lib et pattern que tu m'as consillés, je veux rester dans du QT et du cpp standard.

    Oui je suis en train de recoder tout à la main et intégrer dans mon projet tel que je le veux. Je vais utiliser les functors.

    Je te colle le résultat complet avec les contenu des fichiers ici que tu vois, d'ici là.

    Merci encore.

  12. #12
    Membre habitué
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Juillet 2009
    Messages
    218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Juillet 2009
    Messages : 218
    Points : 130
    Points
    130
    Par défaut
    Merci beaucoup pour vos éclairages.

    J'ai testé et recoder fonctor dans tous les sens.

    Mais ça m’alourdissait le code, et n'était pas adapté à ma situation.

    J'ai utilisé finalement la classe QMetaObject de QT, et ça marche bien aussi.

    Enocre merci.

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

Discussions similaires

  1. Pointeur de fonction membre.
    Par fabone dans le forum C++
    Réponses: 2
    Dernier message: 18/01/2006, 13h18
  2. Réponses: 4
    Dernier message: 01/12/2005, 12h33
  3. Pointeur de fonction membre
    Par legend666 dans le forum C++
    Réponses: 1
    Dernier message: 04/10/2005, 20h46
  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