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 :

Utiliser une méthode d'une classe quelconque comme fonction de callback


Sujet :

Langage C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 37
    Points : 18
    Points
    18
    Par défaut Utiliser une méthode d'une classe quelconque comme fonction de callback
    Bonjour,

    Le net est tellement fourni en documentation et exemples sur le sujet que je n'arrive pas à trouver solution à mon problème. Ou alors il faudrait que je passe une semaine à me former sur le sujet des pointeurs de fonction, des fonctions lambda, peut-être des templates, ce qui me serait surement très bénéfique, mais je manque de temps.

    J'aimerai pouvoir dire à un objet d'une classe A d'appeler une méthode d'une autre classe quelconque.

    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 A {
        void setFunctionToCall( /* paramètres */ ){
            // La fonction passée en paramètre est une méthode non statique d'une classe quelconque
        };
        void f(){
            // Appeler la fonction définie par setFunctionToCall
        };
    };
     
    class B {
        void g(){
            cout << "hourra !" << endl;
        }
    };
     
    class C {
        string message;
        C() : message("youpi !") {}
        void h(){
            cout << message << endl;
        }
    };
     
     
    A a;
    B b;
    a.setFunctionToCall( b, B::g ); // ou quelque chose du genre.
    C c;
    a.setFunctionToCall( c, C::h ); // ou quelque chose du genre.
    J'ai trouvé beaucoup d'exemples pour implémenter la chose pour une classe unique (seulement B, ou seulement C, mais pas les 2 à la fois). Je pourrais aussi utiliser un template pour la classe A qui permettrait d'utiliser B ou C pour des instances différentes, comme
    Mais je souhaite qu'une même instance de A puisse appeler au choix une méthode de n'importe quelle classe, et en changer en cours d'exécution, comme dans le code d'exemple donné.
    Je n'arrive même pas à savoir si c'est possible en C++ !
    Merci beaucoup.

    P.S : J'ai besoin de ceci pour une application construite avec openFrameworks qui intègre la librairie POCO. Peut-être POCO permet-elle de faire ceci. Je préfèrerais apprendre à le faire sans POCO, en utilisant les nouveautés de C++11 si besoin.

  2. #2
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Bonjour

    En passant un pointeur sur fonction membre avec l'objet en question et en enregistrant le code dans une std::function via une lambda, cela fonctionne bien
    (J'ai fait la version const, mais tu pourras adapter le code selon tes besoins)
    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
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++11 -pedantic -fopenmp main.cpp -o main && ./main
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++98 -pedantic -fopenmp main.cpp -o main && ./main
     
    #include <iostream>
    #include <functional>
     
     
    class A
    {
    private:
     
    	std::function<void (void)> m_fct;
     
    public:
     
    	template <class member_fct_t, class T>
    	void setFunctionToCall(member_fct_t const & fct, T const & t)
    	{
    		m_fct = [&]() -> void { (t.*fct)(); };
    	}
     
    	void f() const
    	{
    		m_fct();
    	}
    };
     
    class B
    {
    public:
     
    	void g() const
    	{
    		std::cout << "hourra !" << std::endl;
    	}
    };
     
    class C
    {
    public:
     
    	std::string message;
     
    	C() : message("youpi !") { }
     
    	void h() const
    	{
    		std::cout << message << std::endl;
    	}
    };
     
     
    int main()
    {
    	A a;
     
    	B b;
    	a.setFunctionToCall(&B::g, b);
    	a.f();
     
    	C c;
    	a.setFunctionToCall(&C::h, c);
    	a.f();
     
    	return 0;
    }
    La sortie est celle attendue
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $ g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++11 -pedantic -fopenmp main.cpp -o main && ./main
    hourra !
    youpi !

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 37
    Points : 18
    Points
    18
    Par défaut
    Merci beaucoup Ehonn de cette réponse éclair,
    ça marche !

    Hélas je n'arrive pas à l'adapter à la situation dans laquelle je suis...
    Après plusieurs essais j'ai trouvé ce qui bloque mais ne trouve comment le résoudre.

    Pour commencer j'ai enlevé des const pour être au plus proche de mon cas.
    Ensuite j'ai utilisé un pointeur plutôt qu'une référence dans la méthode d'enregistrement de la fonction.
    Jusque-là ça fonctionne:
    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
    #include <iostream>
    #include <functional>
     
     
    class A
    {
    private:
     
    	std::function<void (void)> m_fct;
     
    public:
     
    	template <class member_fct_t, class T>
    	void setFunctionToCall(member_fct_t const & fct, T * t)
    	{
    		m_fct = [&]() -> void { (t->*fct)(); };
    	}
     
    	void f()
    	{
    		m_fct();
    	}
    };
     
    class B
    {
    public:
     
    	A a;
     
    	void g()
    	{
    		std::cout << "hourra !" << std::endl;
    	}
     
    	void test()
    	{
    		a.f();
    	}
     
    };
     
    int main()
    {
    	B b;
    	b.a.setFunctionToCall(&B::g, &b);
    	b.test();
     
    	return 0;
    }
    Mais dès que j'essaie de faire en sorte que ce soit la classe B qui enregistre elle-même une de ses méthodes le programme compile mais plante:
    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
    #include <iostream>
    #include <functional>
     
    class A // Pas de changements dans cette classe
    {
    private:
     
    	std::function<void (void)> m_fct;
     
    public:
     
    	template <class member_fct_t, class T>
    	void setFunctionToCall(member_fct_t const & fct, T * t)
    	{
    		m_fct = [&]() -> void { (t->*fct)(); };
    	}
     
    	void f()
    	{
    		m_fct();
    	}
    };
     
    class B // Ajout du membre a et de la méthode setup()
    {
    public:
     
    	A a;
     
    	void setup()
    	{
    		a.setFunctionToCall(&B::g, this ); // Est-ce this qui n'est pas le bon choix ?
    	}
     
    	void g()
    	{
    		std::cout << "hourra !" << std::endl;
    	}
     
    	void test()
    	{
    		a.f();
    	}
     
    };
     
    int main()
    {
    	B b;
    	b.setup();
    	b.test();
     
    	return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    mingw32-g++.exe -std=c++11 -Wall -fexceptions  -std=c++11 -g     -c E:\cpp\essais\callback1\main.cpp -o obj\Debug\main.o
    mingw32-g++.exe  -o bin\Debug\callback1.exe obj\Debug\main.o
     
    #0 0028FEB1	?? () (??:??)
    #1 00460E40	std::_Function_handler<void (), void A::setFunctionToCall<void (B::*)(), B>(void (B::* const&)(), B*)::{lambda()#1}>::_M_invoke(std::_Any_data const&)(__functor=...) (c:/program files (x86)/codeblocks-12.11/mingw/bin/../lib/gcc/mingw32/4.7.1/include/c++/functional:1926)
    #2 00434E5E	std::function<void ()>::operator()() const(this=0x28ff00) (c:/program files (x86)/codeblocks-12.11/mingw/bin/../lib/gcc/mingw32/4.7.1/include/c++/functional:2311)
    #3 004201FF	A::f(this=0x28ff00) (E:\cpp\essais\callback1\main.cpp:20)
    #4 00420277	B::test(this=0x28ff00) (E:\cpp\essais\callback1\main.cpp:42)
    #5 0040139B	main() (E:\cpp\essais\callback1\main.cpp:51)
    Saurais-tu m'éclairer sur la raison de ceci ?

  4. #4
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Ton code me paraît correct. Il compile est exécute correctement chez moi avec GCC 4.9. En revanche, avec GCC 4.7, il y a une erreur de segmentation (je pense que c'est un bug de GCC).
    Mets à jour ton compilateur.
    Je pense que tu peux / dois utiliser une référence ici (et pas un pointeur), cela a du sens du point de vue de la durée de vie des objets.

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 37
    Points : 18
    Points
    18
    Par défaut
    C'était bien ça.
    Grand merci encore pour ton aide, premier pas pour moi dans le domaine des fonctions lambda et de l'usage de std::function.

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 35
    Points : 28
    Points
    28
    Par défaut
    C'est très intéressant comme bout de code...

    Mais comment l'adapter pour qu'il fonctionne si, par exemple, on a deux méthodes avec des signatures différentes dans la classe B ?

    ex.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    void g() const
    	{
    		std::cout << "hourra !" << std::endl;
    	}
    void g(float value) const
    	{
    		std::cout << "hourra float !" << std::endl;
    	}
    J'ai eu beau tenter des trucs, je n'y suis pas arrivé...

  7. #7
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Le plus simple est de passer directement une lambda :
    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
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++11 -pedantic -fopenmp main.cpp -o main && ./main
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++98 -pedantic -fopenmp main.cpp -o main && ./main
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++98 -pedantic main.cpp -o main && valgrind ./main
     
    #include <iostream>
    #include <functional>
     
     
    class A
    {
    private:
     
    	std::function<void (void)> m_fct;
     
    public:
     
    	template <class fct_t>
    	void setFunctionToCall(fct_t const & fct)
    	{
    		m_fct = fct;
    	}
     
    	void f()
    	{
    		m_fct();
    	}
    };
     
    class B
    {
    public:
     
    	A a0;
    	A a1;
     
    	void setup()
    	{
    		a0.setFunctionToCall([&]() -> void { g(); });
    		a1.setFunctionToCall([&]() -> void { g(0.f); });
    	}
     
    	void g()
    	{
    		std::cout << "hourra !" << std::endl;
    	}
     
    	void g(float const f)
    	{
    		std::cout << "hourra (float = " << f << ") !" << std::endl;
    	}
     
    	void test()
    	{
    		a0.f();
    		a1.f();
    	}
    };
     
    int main()
    {
    	B b;
    	b.setup();
    	b.test();
     
    	return 0;
    }

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    35
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 35
    Points : 28
    Points
    28
    Par défaut
    Par rapport à ton code de départ, maintenant B connait A... Imaginons qu'on puisse traiter n'importe classe, sans la connaitre, est-ce possible ?

    En code fictif ça donnerait

    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
     
    int main()
    {
    	A a;
     
    	B b;
    	a.setFunctionToCall(&B::g<int>, b);
            // ou bien
            a.setFunctionToCall(&B::g<float>, b);
    	a.f();
     
    	C c;
    	a.setFunctionToCall(&C::h<void>, c);
    	a.f();
    }
    D'après toi est-ce réalisable ?

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

Discussions similaires

  1. Utiliser une méthode d'une instance, dans une callback
    Par qdaemon_fr dans le forum Langage
    Réponses: 3
    Dernier message: 18/04/2014, 18h09
  2. Réponses: 0
    Dernier message: 30/09/2009, 18h42
  3. modifier un élément d'une form dans une méthode d'une autre form
    Par baldebaran dans le forum Windows Forms
    Réponses: 9
    Dernier message: 14/08/2009, 13h59
  4. Réponses: 6
    Dernier message: 20/04/2007, 15h24
  5. "ajouter une méthode dans une méthode"
    Par Zorgloub dans le forum Langage
    Réponses: 1
    Dernier message: 09/04/2006, 12h53

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