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 :

Pointeur de function et methode de classe


Sujet :

C++

  1. #1
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut Pointeur de function et methode de classe
    Bonjour,

    J'ai défini un namespace comportant tout un ensemble de fonctions numériques dont certaines prennent des fonctions en arguments:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    namespace numeric
    {
      void f1();
      void f2();
      // ...
      double integration(double (*func)(double));
    }
    Ensuite dans une class C j'ai:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    using namespace numeric;
     
    class C
    {
      // ....
      double function(double d);
      void example();
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void C::example()
    {
      double d = integration(function); // ERROR
    }
    Le problème est que dans la fonction example, là où on attend du double (*)double on reçoit du double ( C:: )double, d'où erreur à la compilation.

    Ainsi, je peux régler cela en modifiant la fonction function en en faisant une fonction libre en dehors de la classe C.
    Cela compile mais je n'ai plus accès aux variables membres de la classe C (j'en ai besoin).

    J'ai essayé d'utiliser les template sans succès (error:<unresolved overloaded function type>).
    J'ai réfléchi à utiliser une fonction membre static mais cela impliques des variables static aussi.

    Vous l'aurez compris, je cherche un moyen de passer par arguments des méthodes membres à une fonction libre (définie dans un fichier et namespace à part).

    Merci de votre aide.

    EDIT 1
    J'ai trouvé une solution pour effectuer l'appel en changeant la signature de integration:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class C;
     
    namespace numeric
    {
      void f1();
      void f2();
      // ...
      double integration(double (C::*func)(double));
    }
    Par contre dans le corps de integration, je n'arrive plus à appeler la fonction en argument:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    double integration(double (C::*func)(double))
    {
      double x = ....;
      double f = (*func)(x); // NE MARCHE PLUS
    }
    Il faut modifié l'appel mais je vois pas comment.

    EDIT 2

    Logiquement je pensais que cela devait être:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      double f = (C.*func)(x);
    Mais ça ne marche pas non plus
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error: expected primary-expression before ‘.*’ token

  2. #2
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Mais C::function() prend deux arguments : un C* caché et un double.
    -- Yankel Scialom

  3. #3
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    En fait pour que cela marche je pense qu'il me faudrait une instance:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    double integration(double (C::*func)(double), C obj)
    {
        double f = (obj.*func)(x);
    }
    Mais je souhaiterai éviter cela et garder la signature:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double integration(double (C::*func)(double));

    EDIT
    J'ai vu la manière de faire suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        double f = (*this.*func)(x);
    Mais dans mon cas this n'as pas de sens car il s'agit d'une fonction libre (pas de classe), et donc j'ai le mesage d'erreur suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error: invalid use of ‘this’ in non-member function

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Essaie d'utiliser un type template en guise de fonction.
    L'intérêt, c'est que tu pourras lui donner un foncteur.

    exemple de foncteur (un peu a l'ancienne):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Fonction {
    private:
        C const& ref
    public:
        Fonction(C const& ref) : ref(ref) {}
        double Fonction(double d) const {return ref.function(d);}
    };
    En fait, il s'agit grosso modo de ce que fait/faisait std::mem_fun
    Ca peut certainement se remplacer par une lambda, mais je ne sais plus l'écrire
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    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
    En C++11, tu peux passer 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
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++11 -pedantic main.cpp -o main && ./main
     
    #include <iostream>
     
    class C
    {
    public:
     
    	void function(double const d) const { std::cout << "C::function(" << d << ")" << std::endl; }
    };
     
    template <class fct_t>
    void integration(fct_t && f)
    {
    	f(21.42);
    }
     
    int main()
    {
    	integration([](double const d) { C().function(d); });
     
    	return 0;
    }

  6. #6
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Merci de votre aide.

    Je dois rester en C++98, du coup je vais plutôt m'orienter du côté du foncteur, par contre j'ai pas bien compris dans mon cas ce que je dois faire.

    J'ai d'une part des fonctions libres .h/.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    namespace numeric
    {
      void f1();
      ...
    }
    Et de l'autre une classe C
    Je dois définir le foncteur où?

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 629
    Points : 10 554
    Points
    10 554
    Par défaut
    Et en utilisant une fonction copine? , si j'ai bien compris ton problème


    Code approximatif:
    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
     
    void g_callback(A& a) {
        a.dummy = 5;
    }
     
     
    class A {
        friend void g_callback(A& a);
     
    public:
     
        A() : dummy(0) {}
     
    private:
     
        int dummy;
    };

  9. #9
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Il me semble que le friend ne sert qu'entre deux classes, alors quen occurence ici j'ai qu'une classe d'un côté un des fonctions libres de l'autre.

  10. #10
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Citation Envoyé par LinuxUser Voir le message
    Il me semble que le friend ne sert qu'entre deux classes, alors quen occurence ici j'ai qu'une classe d'un côté un des fonctions libres de l'autre.
    nop.

    Mais le problème n'est pas même déplacé : la fonction libre a besoin d'une instance.
    -- Yankel Scialom

  11. #11
    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
    La lambda de C++11 n'est pas obligatoire, on peut utiliser la même astuce avec C++98, avec ou sans Boost
    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
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++98 -pedantic main.cpp -o main && ./main
     
    #include <iostream>
    #include <functional>
    #include <boost/bind.hpp>
     
    class C
    {
    public:
     
    	void function(double const d) const { std::cout << "C::function(" << d << ")" << std::endl; }
    };
     
    template <class fct_t>
    void integration(fct_t const & f)
    {
    	f(21.42);
    }
     
    int main()
    {
    	// C++11 (lambda)
    	// integration([](double const d) { C().function(d); });
     
    	// C++98
    	{
    		C c;
    		integration(std::bind1st(std::mem_fun(&C::function), &c));
    	}
     
    	// C++98 avec Boost
    	{
    		C c;
    		integration(boost::bind(&C::function, &c, _1));
    	}
     
    	return 0;
    }

  12. #12
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonjour,

    Tentative avec une approche compatible Lambda, foncteur, bind, etc. :

    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
     
    #include <iostream>
     
    /**
     * integration sur [0,1]
     */
    template < typename F >
    double integration( F f, const int & N = 100 ){
    	const double start = 0.0 ;
    	const double end   = 1.0 ;
     
    	double result = 0.0 ;
    	double dx = (end-start) / ( N - 1 ) ; 
    	for ( int i = 0; i < N-1; i++ ){
    		double x1 = start + i * dx ;
    		double x2 = start + (i+1) * dx ;
    		result += 0.5 * ( f(x1) + f(x2) ) * dx ;
    	}
    	return result ;
    }
     
    struct One {
    	double operator () ( double /*x*/ ) const {
    		return 1.0; 
    	}
    } ;
     
    struct Identity {
    	double operator () ( double x ) const {
    		return x ;
    	}
    } ;
     
     
    double square( double x ){
    	return x * x ;
    }
     
    // g++ -Wall --std=c++11 -o integration main.cpp && integration.exe
    int main( int argc, char* argv[] ){
    	{
    		One f;
    		std::cout << "integration(One) : " << integration(f) << std::endl ;
    		std::cout << "attendu : " << 1.0 << std::endl;
    	}
    	{
    		Identity f;
    		std::cout << "integration(Identity) : " << integration(f) << std::endl ;
    	}
    	{
    		std::cout << "integration(Square) : " << integration(square) << std::endl ;
    	}
    	{
    		std::cout << "integration(Cube) : " << integration(
    			[] (double x) -> double { 
    				return x*x*x; 
    			}) << std::endl ;
    	}
    	return 0 ;
    }

  13. #13
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Hello

    Si ta fonction a besoin d'une instance, ce n'est pas une fonction libre ! Si tu tiens à ce que tes fonctions soient membres d'une classe mais puissent être appelées sans instance, alors tu as un problème de conception, tu cherches à résoudre un problème de la mauvaise manière.
    Find me on github

  14. #14
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Salut,

    Je pense être malheureusement arrivé à la même conclusion
    Du coup , soit je revois le design, soit j'essaye de trouver une solution technique au problème.

  15. #15
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Tu pourrais --- mais c'est bancal, non réentrant, peu maintenable, peu compréhensible --- ajouter à C un membre statique qui servirait à mémoriser l'instance en question :
    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
    namespace ns
    {
        double functor(double (*f)(double));
    }
     
    class C
    {
        static const C* _instance;
        double f_impl(double) const;
    public:
        static double f(double d) { return _instance->f_impl(d); }
        void exemple(void)
        {
            _instance = this;
            ns::functor(f);
        }
    };
    -- Yankel Scialom

  16. #16
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    J'ai du mal à voir où tu veux en venir, car l'instance on en a besoin dans la fonction libre, pas dans la classe elle-même.
    Par contre l'idée d'instance static j'y avais pensé en faisant un genre de singleton que je récupèrerais via une variable globale (commune à la fonction libre et la classe instanciant la classe C), mais bon je m'égare là c'est mal.

  17. #17
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Citation Envoyé par LinuxUser Voir le message
    J'ai du mal à voir où tu veux en venir, car l'instance on en a besoin dans la fonction libre, pas dans la classe elle-même.
    Par contre l'idée d'instance static j'y avais pensé en faisant un genre de singleton que je récupèrerais via une variable globale (commune à la fonction libre et la classe instanciant la classe C), mais bon je m'égare là c'est mal.
    Je te fais confiance pour adapter : plutôt qu'une fonction libre, un object foncteur avec une instance de C.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Foncteur
    {
        C const& _instance;
    public:
        Foncteur(C const& c) : _instance(c) {}
        double operator()(double d) { return c.f(d); }
    };
    Ça n'a pas déjà été proposé ? je m'emmêle ...
    -- Yankel Scialom

  18. #18
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Si, je l'ai fait, mais j'avais bêtement appelé l'opérateur fonction au lieu de operator()Du coup, ca marchait moins bien ...
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  19. #19
    Membre confirmé Avatar de LinuxUser
    Inscrit en
    Avril 2007
    Messages
    857
    Détails du profil
    Informations forums :
    Inscription : Avril 2007
    Messages : 857
    Points : 616
    Points
    616
    Par défaut
    Je comprends l'idée du foncteur, mais je vois pas comment l'appliquer dans mon cas.

    Il faudrait que au lieu d'avoir un fichier de fonctions libres, je devrais en faire une classe où je surcharge l'operateur () comment indiqué plus haut?

  20. #20
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Citation Envoyé par LinuxUser Voir le message
    Il faudrait que au lieu d'avoir un fichier de fonctions libres, je devrais en faire une classe où je surcharge l'operateur () comment indiqué plus haut?
    Regarde mon exemple au dessus, la magie s'opère sur la fonction "integration" qui permet d'utiliser en paramètre soit des fonctions libres, soit des bind, soit des lambda, soit des foncteurs,... La fonction d'intégration s'en moque : Il suffit que la fonction à intégrer s'appelle sous la forme double y = f(x).

    Pour t'adapter à ça avec des méthodes sur des classes, tu te retrouves à passer d'un appel sous la forme c.fonction(x); à un appel sous la forme f(x). Pour ça, si tu ne veux pas faire des wrappers à la main sous forme de foncteur (chose assez pénible), il y a deux solutions propres illustrées par Ehonn : std::bind (après C++11), boost::bind (avant C++11).

    Cette notion de bind est d'ailleurs omniprésente quand tu lis des documentations sur les threads à la sauce boost ou C++11 : Tu gagneras plus à te mettre à ça qu'en magouillant des pointeurs de fonction "C-like"...

Discussions similaires

  1. Réponses: 9
    Dernier message: 08/02/2006, 12h46
  2. [POO] Prob avec une méthode de classe
    Par Ludo75 dans le forum Langage
    Réponses: 9
    Dernier message: 06/02/2006, 22h37
  3. [DLL] Methodes de classe et dll
    Par chronos dans le forum C++
    Réponses: 11
    Dernier message: 27/04/2005, 17h18
  4. Méthode de classe et copie d'objets
    Par Duloup dans le forum Général Python
    Réponses: 5
    Dernier message: 11/04/2005, 16h27
  5. Visibilité de methodes dans Classe de Class
    Par Math75 dans le forum C++
    Réponses: 9
    Dernier message: 28/09/2004, 12h48

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