Pointeur de fonction membre sur fonction membre de meme classe

Pointeur de fonction membre sur fonction membre de meme classe - C++ - Programmation

Marsh Posté le 31-01-2005 à 19:07:26    

Bonjour,
Je voudrais faire une chose comme ce qui suit mais j'ai un probleme et ne sait pas comment faire appel au pointeur sur fonction membre... Si vous avez une idee...

Code :
  1. class toto
  2. {
  3.    public :
  4.       toto( bool b ) {
  5.          if (b) d = &toto::disp1 ;
  6.          else d = &toto::disp2 ;
  7.       }
  8.       typedef void (toto::*D)() ;
  9.       D d ;
  10.    protected :
  11.       void disp1(void) { cout << "Disp1" << endl ; }
  12.       void disp2(void) { cout << "Disp2" << endl ; }
  13. } ;
  14. int
  15. main()
  16. {
  17.    toto t ;
  18.    (t.*d)() ;  // <-- probleme ici
  19.    return 0;
  20. }


Reply

Marsh Posté le 31-01-2005 à 19:07:26   

Reply

Marsh Posté le 31-01-2005 à 22:50:54    

boot::bind est ton amie dans ce cas (c'est peut être possible directement avec la STL mais je sais pas faire)
 
En gros ton problème est que dans ton initialisation de d, tu ne précise pas de quel objet est issue la fonction que tu souhaite appeler. Si ça peut t'aider ton truc marcherait bien avec des fonctions statiques mais il manque le pointeur this implicite dans ton cas.
 
Un code utilisant boost::bind et boost::function :

Code :
  1. #include <iostream>
  2. #include <boost/bind.hpp>
  3. #include <boost/function.hpp>
  4. using namespace std;
  5. class toto
  6. {
  7. public:
  8.   toto(bool b) {
  9.     if(b)
  10.       d = boost::bind(&toto::disp1,this);
  11.     else
  12.       d = boost::bind(&toto::disp2,this);
  13.   }
  14.  
  15. //  typedef void(*D)(void);
  16.   typedef boost::function<void(void)> D;
  17.   D d;
  18. protected:
  19.   void disp1() { cout << "Disp1" << endl; }
  20.   void disp2() { cout << "Disp2" << endl; }
  21. };
  22. int
  23. main()
  24. {
  25.   toto t(false);
  26.   (t.d)();
  27.   return 0;
  28. }


Message édité par gatorette le 31-01-2005 à 22:51:30

---------------
each day I don't die is cheating
Reply

Marsh Posté le 31-01-2005 à 23:10:15    

boost, c'est génial.
 
Mais bon, faudrait déjà crriger l'exemple de base  
 
(t.*(t.d))() ;

Reply

Marsh Posté le 31-01-2005 à 23:12:00    

pour en revenir à l'exemple de gatorette, tout l'intérêt, c'est ensuite de pouvoir écrire
 
  boost::function<void(void)> e = t.d;
 
  e();

Reply

Marsh Posté le 31-01-2005 à 23:30:42    

Taz a écrit :

boost, c'est génial.


 :jap:  
 

Taz a écrit :


Mais bon, faudrait déjà crriger l'exemple de base  
 
(t.*(t.d))() ;


Et juste un t.d() comme dans mon exemple ? C'est incorrect ?
Parce que sur mon compilateur (VC++ 7.1) ça compile sans problème.


---------------
each day I don't die is cheating
Reply

Marsh Posté le 31-01-2005 à 23:39:15    

c'est correcte. t.d est la fonction disp liée à l'instance t. On a ça dans plein de langage comme python par exemple. Je souligné juste que l'intérêt, c'est de pouvoir manipuler (t.d) comme un objet indépendant et en faire ce qu'on veut.

Reply

Marsh Posté le 01-02-2005 à 01:11:20    

Taz a écrit :

c'est correcte. t.d est la fonction disp liée à l'instance t. On a ça dans plein de langage comme python par exemple. Je souligné juste que l'intérêt, c'est de pouvoir manipuler (t.d) comme un objet indépendant et en faire ce qu'on veut.


 :jap:  
 
OK... sinon j'ai essayé de faire ça en utilisant que la STL mais je n'ai pas réussi. Voici ce que j'ai fait jusqu'à présent :

Code :
  1. #include <iostream>
  2. #include <functional>
  3. using namespace std;
  4. class toto
  5. {
  6. public:
  7.   toto(bool b)
  8.     : d_(bind1st(mem_fun(&toto::disp1),this))
  9.   {
  10.     if(!b)
  11.       d_ = bind1st(mem_fun(&toto::disp2),this);
  12.   }
  13.   typedef binder1st<mem_fun1_t<void,toto,int> > D;
  14.   D d_;
  15. private:
  16.   void disp1(int) { cout << "disp_1" << endl; }
  17.   void disp2(int) { cout << "disp_2" << endl; }
  18. };
  19. int main()
  20. {
  21.   toto t(true);
  22.   t.d_(5);
  23.   return 0;
  24. }


 
Deux gros problèmes :  

  • J'ai été obligé de mettre un paramètre (int) à mes fonctions car sinon le bind1st (indispensable pour lier le this à la fonction) n'a pas assez d'arguments. En effet, il exécute une fonction "binaire" (mon mem_fun_t acceptant comme argument mon pointeur vers mon objet toto et le int que j'ai rajouté) en simulant une fonction "unaire". La solution serait-elle de refaire une classe bindsolearg qui permettrait de binder à la création de l'objet le seul argument de la fonction qui nous intéresse ?
  • Comme un binder1st n'a pas de constructeur par défaut (contrairement à boost::function par exemple), il faut que je l'initialise à la construction. Or je suis obligé de mettre une fonction membre de toto (à cause du mem_fun_t) et mon toto n'est pas encore complètement initialisé. VC++ me met même un warning lors de  la compilation mais le programme semble tourner.


-----------------
v2
Bon en écrivant le message j'ai réflechi un peu et voici une version qui corrige les deux problèmes évoqués ci-dessus :

Code :
  1. #include <iostream>
  2. #include <functional>
  3. using namespace std;
  4. template<typename Operation>
  5. class bindsolearg
  6. {
  7. public:
  8.   typedef typename Operation::result_type result_type;
  9.   bindsolearg()
  10.     : op(0)
  11.   { }
  12.   bindsolearg(const Operation & _Func, const typename Operation::argument_type& _Arg)
  13.     : op(_Func), val(_Arg)
  14.   { }
  15.   result_type operator()() const
  16.   {
  17.     op(val);
  18.   }
  19. protected:
  20.   Operation op;
  21.   typedef typename Operation::argument_type value;
  22.   value val;
  23. };
  24. template<class Operation, class Type>
  25. bindsolearg<Operation> bindarg(const Operation& _Func, const Type& _Arg)
  26. {
  27.   return bindsolearg<Operation>(_Func,_Arg);
  28. }
  29. class toto
  30. {
  31. public:
  32.   toto(bool b)
  33.   {
  34.     if(!b)
  35.       d_ = bindarg(mem_fun(&toto::disp2),this);
  36.     else
  37.       d_ = bindarg(mem_fun(&toto::disp1),this);
  38.   }
  39.   typedef bindsolearg<mem_fun_t<void,toto> > D;
  40.   D d_;
  41. public:
  42.   void disp1() { cout << "disp_1" << endl; }
  43.   void disp2() { cout << "disp_2" << endl; }
  44. };
  45. int main()
  46. {
  47.   toto t(true);
  48.   t.d_();
  49.   return 0;
  50. }


 
Donc cette version fonctionne à peu près correctement. A priori le seul "petit" souci est que mon objet functor peut ne pas être initialsé (en utilisant le constructeur par défaut) et donc si on appelle la fonction ça plante misérablement. Dans un sens on peut arguer que si l'objet n'est pas initialisé c'est qu'il y a un défaut de conception mais la méthode utilisée dans boost::function (exception générée) est beaucoup plus propre et exploitable.
 
Bon au final je pense qu'on peut conclure que Boost  :love:  est vraiment indispensable !


---------------
each day I don't die is cheating
Reply

Marsh Posté le 01-02-2005 à 01:14:48    

Bonne conclusion. <functional> c'est pas mal au sein d'expression simple, comme par exemple en argument temporaire de std::copy. Dès qu'on veut aller plus loin, ou utiliser avec des fonctions (même membres) avec des paramètres variés, const, , références, etc, c'est l'enfer. Il y a d'ailleurs des défaillances de STL là dessus. D'où http://boost.org/libs/functional/index.html
 
Boost::function<> est vraiment très pratique.
 
 
edit: sinon c'est très bien. C'est exactement l'approche qu'il faut avoir.
 
J'ai un problème. J'essaie de les résoudre avec les outils que je maîtrise déjà. Je me rends compte que mes outils ne sont pas suffisants. J'essaie de fabriquer un nouvel outil plus puissant. Je touche du doigts la technologie nécessaire à résoudre mon problème. Je ne suis pas victime du NIH. J'utilise une implémentation populaire qui a fait ses preuves. Je maîtrise un nouvel outil.


Message édité par Taz le 01-02-2005 à 01:21:22
Reply

Marsh Posté le 01-02-2005 à 09:33:27    

Merci a vous deux !
 
Merci Taz pour la correction :

Citation :

(t.*(t.d))() ;


et merci gatorette pour tes exemples explicites.
 
Je vais regarder cette boost d'un peu plus pres que ce que j'ai fait jusqu'a present.
@+
BB138

Reply

Marsh Posté le 28-05-2008 à 15:43:44    

C'est peut etre un peut tard pour reouvrir ce post, mais j'ai une question tout à fait similaire, concernant boost::signal
 
je souhaite binder une methode membre (une callback) à un signal boost, par .connect  
Mais je ne parviens pas à lui passer le pointeur vers ma fonction comme il se doit.

Reply

Marsh Posté le 28-05-2008 à 15:43:44   

Reply

Marsh Posté le 28-05-2008 à 20:19:17    

Exemple pour un signal de envoyant un float en paramètre:

Code :
  1. boost::signal<void (float)> sig;
  2. sig.connect(boost::bind(&LaClasse::UneFonction, instanceDeLaClasse, _1)); // où _1 correspond à l'argument qui sera transmis
  3. sig(3.14f);


Message édité par IrmatDen le 28-05-2008 à 20:19:41
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed