OVH Cloud OVH Cloud

Pointeur sur fonction membre

10 réponses
Avatar
Boris Sargos
Salut à tous,

dans une de mes classes, j'ai besoin de stocker l'adresse d'une fonction
membre d'une autre classe. Sachant que les deux classes en question ne se
connaissent pas (et c'est important), je souhaiterais naturellement écrire
un truc du genre :


Class A {
private :
double& GetA() { return m_A; }
double m_A;
}

Class B {
public:
B(double&(*f)(void)) { GetA = f; } // Constructeur
private :
double&(*GetA)(void);
}

Mais ce code ne compile pas. Je n'ai apparamment pas le droit d'écrire un
tel truc. Je ne comprends pas pourquoi.
Quelqu'un pourrait-il m'expliquer ce qui ne va pas, et comment y palier ?

Note : j'ai également essayé avec les classes mem_fun_t et compagnie
fournies par la stl. Ca n'a pas non plus fonctionné.

Merci de votre aide.

10 réponses

Avatar
Michel Michaud
Dans news:c7ifeg$iog$, Boris
Salut à tous,

dans une de mes classes, j'ai besoin de stocker l'adresse d'une
fonction membre d'une autre classe. Sachant que les deux
classes en question ne se connaissent pas (et c'est important),
je souhaiterais naturellement écrire un truc du genre :


Class A {
private :
double& GetA() { return m_A; }
double m_A;
}

Class B {
public:
B(double&(*f)(void)) { GetA = f; } //
Constructeur private :
double&(*GetA)(void);
}

Mais ce code ne compile pas. Je n'ai apparamment pas le droit
d'écrire un tel truc. Je ne comprends pas pourquoi.


Un pointeur sur une fonction membre, ce n'est pas la même chose
qu'un pointeur sur une fonction. Tu ne peux pas garder l'un
dans une variable pour l'autre. Si les deux classes ne se
connaissent pas, je ne vois pas comment tu pourras faire parce
que pour déclarer le pointeur sur une fonction, il faut avoir
son type et il dépend de la classe... Je vois bien comment
recevoir le pointeur (avec une fonction template), mais je ne
vois pas comment le storer dans un variable membre d'une classe
ordinaire (on pourrait le faire avec une classe template, mais
je ne crois pas que ça soit un choix possible dans ton cas). Il
est tôt pour moi, alors peut-être que quelqu'un d'autre aura
une meilleure idée !

Que voulais-tu faire exactement ? Peut-être qu'il y a une autre
solution (je pense au pattern Visitor par exemple...).

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Michel Michaud
Dans news:Qu3nc.100362$, Michel
Un pointeur sur une fonction membre, ce n'est pas la même chose
qu'un pointeur sur une fonction. Tu ne peux pas garder l'un
dans une variable pour l'autre. Si les deux classes ne se
connaissent pas, je ne vois pas comment tu pourras faire parce
que pour déclarer le pointeur sur une fonction, il faut avoir


Lire « sur une fonction MEMBRE »

son type et il dépend de la classe...


--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Gregory Montoir
Boris Sargos wrote:
Salut à tous,

dans une de mes classes, j'ai besoin de stocker l'adresse d'une fonction
membre d'une autre classe. Sachant que les deux classes en question ne se
connaissent pas (et c'est important), je souhaiterais naturellement écrire
un truc du genre :
[...]
Mais ce code ne compile pas. Je n'ai apparamment pas le droit d'écrire un
tel truc. Je ne comprends pas pourquoi.
Quelqu'un pourrait-il m'expliquer ce qui ne va pas, et comment y palier ?


Ca me parait un peu bizarre ce que tu cherches a faire... Enfin, si
c'est juste un probleme de syntaxe, essaye un truc comme ca :

class A {
public:
A(double d) : d_(d) {}
double& get() { return d_; }
private:
double d_;
};

typedef double& (A::*GetDouble)();

class B {
public:
B(GetDouble gd) : gd_(gd) {}
GetDouble GetDoubleProc() { return gd_; }
private:
GetDouble gd_;
};

int main() {
A a1(3.14);
A a2(0.);
B b(&A::get);
GetDouble gd = b.GetDoubleProc();
std::cout << (a1.*gd)() << std::endl << (a2.*gd)() << std::endl;
return 0;
}

$ g++ b.cpp && a
3.14
0

En esperant que ca te fasse avancer...

Avatar
Boris Sargos
Merci à vous deux pour votre aide. J'ai compris que j'ai fait une faute de
structuration, et que je n'ai pas choisi la bonne implémentation.
Je vais réfléchir à une autre solution.

Merci.
Avatar
drkm
"Michel Michaud" writes:

Je vois bien comment
recevoir le pointeur (avec une fonction template)


Mais si B ne connait pas A à la compilation ? Je ne suis pas
certain de voir exactement ce à quoi tu penses. Peux-tu être plus
précis, s'il te plaît ?

Il me semble que si le coup du modèle de fonction marche, un modèle
de classe imbriqué devrait convenir pour le stockage. Reste la
question de l'exécution de la fonction pointée, pour laquelle un objet
est requis.

Mais je ne vois pas bien l'intérêt de garder un pointeur sur
fonction membre d'une classe inconnue. Peux-tu (le PO) décrire plus
précisément ce que tu veux faire ?

--drkm

Avatar
drkm
Gregory Montoir writes:

class A {
public:
A(double d) : d_(d) {}
double& get() { return d_; }
private:
double d_;
};

typedef double& (A::*GetDouble)();

class B {
public:
B(GetDouble gd) : gd_(gd) {}
GetDouble GetDoubleProc() { return gd_; }
private:
GetDouble gd_;
};

int main() {
A a1(3.14);
A a2(0.);
B b(&A::get);
GetDouble gd = b.GetDoubleProc();
std::cout << (a1.*gd)() << std::endl << (a2.*gd)() << std::endl;
return 0;
}

$ g++ b.cpp && a
3.14
0

En esperant que ca te fasse avancer...


Mais le PO a précisé que B ne « connait pas » A. Je ne sais pas
trop que penser à ce sujet. S'il veut dire que A n'est pas déclaré
dans l'UC où l'on utilise B, je ne vois pas comment régler son
problème.

J'ai l'impression qu'il cherche un moyen de reporter à l'exécution
la fonction membre choisie *et* le type de l'objet de cette fonction.
Un genre de template dynamique. Et là, je ne vois pas de solution ;
je pense d'ailleurs qu'il n'y en a pas.

--drkm

Avatar
Boris Sargos
Merci de tenter de répondre à ma requête.


Je vois bien comment
recevoir le pointeur (avec une fonction template)


Mais si B ne connait pas A à la compilation ? Je ne suis pas
certain de voir exactement ce à quoi tu penses. Peux-tu être plus
précis, s'il te plaît ?


En fait, vous avez raison, j'ai trop simplifié mon exemple : A connaît B,
mais B ne connaît pas A.
Pour être plus précis, B est un itérateur de A :

Class MyVector { // la classe A
public:
MyIterator begin() { return MyIterator(0, &GetValue); }
double& operator [] (int i) { return GetValue(i); }
private :
double& GetValue(int i) { return UN_DOUBLE; }
vector<double>* m_pMyTab;
}

Class MyIterator { // la classe B
public:
MyIterator (double&(*f)(void)) { ref = f; } //
Constructeur
double & operator * ( return ref(m_Current); } //
Appelle en fait MyVector::GetValue(m_Current)
MyIterator& operator ++ () { m_Current++; return *this; }
private :
double&(*ref)(void);
int m_Current;
}

Là encore, j'ai simplifié. Mais il est clair que MyIterator ne peut pas
connaître MyVector.
La fonction MyVector::GetValue(int i) renvoie un élément du tableau m_pMyTab
mais pas nécessairement le i-ème. C'est bien sûr pour cette raison que le
tout est si compliqué : je suis obligé de créer mon propre itérateur pour
tenir compte de cet index variable.
Par exemple, cela peut être :

double& GetValue(int i) {
return (*m_pMyTab)[random(m_pMyTab->size()-1];
}

Où random(int i) retourne un nombre choisi aléatoirement entre 0 et i.
J'aurais dû, alors, écrire directement la fonction GetValue dans la classe
itérateur MyIterator. Mais dans ce cas, je n'aurais pas pu en bénéficier
lors de la surcharge de [] dans la classe MyVector.

Est-ce plus clair ?
Merci.


Avatar
drkm
"Boris Sargos" writes:

Je vois bien comment
recevoir le pointeur (avec une fonction template)


Mais si B ne connait pas A à la compilation ? Je ne suis pas
certain de voir exactement ce à quoi tu penses. Peux-tu être plus
précis, s'il te plaît ?


En fait, vous avez raison, j'ai trop simplifié mon exemple : A connaît B,
mais B ne connaît pas A.
Pour être plus précis, B est un itérateur de A :

Class MyVector { // la classe A
public:
MyIterator begin() { return MyIterator(0, &GetValue); }
^^^^^^^^^^^^


Le constructeur de MyIterator ne prend qu'un paramètre. Mais ce
n'est sans doute qu'une erreur de recopiage lors de la simplification
du code.

double& operator [] (int i) { return GetValue(i); }
private :
double& GetValue(int i) { return UN_DOUBLE; }
vector<double>* m_pMyTab;
}

Class MyIterator { // la classe B
public:
MyIterator (double&(*f)(void)) { ref = f; } //
^^^^

Constructeur
double & operator * ( return ref(m_Current); } //
^^^^^^^^

Appelle en fait MyVector::GetValue(m_Current)
MyIterator& operator ++ () { m_Current++; return *this; }
private :
double&(*ref)(void);
^^^^


GetValue prend un entier en paramètre. La simplification, encore,
j'imagine.

int m_Current;
}

Là encore, j'ai simplifié. Mais il est clair que MyIterator ne peut pas
connaître MyVector.


Pourquoi ? Typiquement, j'utiliserais ici un modèle de classe (si
du moins aucune subtilité ne m'a échappée).

template< class Container , typename Type >
class MyIterator
{
public:
typedef Type & ( Container::* Accessor )( int ) ;

MyIterator( Container & c , Accessor a )
 : myCont( c )
, myAccess( a )
, myIndex( some_value )
{
}

Type & operator*()
{
return myCont.*myAccess( myIndex ) ;
}

MyIterator & operator++() ;
MyIterator & operator++( int ) ;

private:
Container & myCont ;
Accessor myAccess ;
int myIndex ;
} ;

Du moins si l'on tient au pointeur sur fonction. Personnellement,
je pense qu'un paramètre template supplémemtaire pour renseigner un
foncteur serait plus souple.

template
<
class Container_t ,
typename Accessor_t = ContainerTraits< Container >::Accessor
typename Type_t = ContainerTraits< Container >::Type

class MyIterator

{
public:
typename Container_t Container ;
typename Accessor_t Accessor ;
typename Type_t Type ;

MyIterator( Container & c )
 : myCont( c )
, myAccess( Accessor() )
{
}

MyIterator( Container & c , Accessor const & a )
 : myCont( c )
, myAccess( a )
{
}

Type & operator*()
{
return myAccess() ;
}

Type & operator->()
{
return & myAccess() ;
}

MyIterator & operator++() ; // redirection vers myAccess
MyIterator operator++( int ) ; // redirection vers myAccess

private:
Container & myCont ;
Accessor myAccess ;
} ;

Code non testé.

La fonction MyVector::GetValue(int i) renvoie un élément du tableau m_pMyTab
mais pas nécessairement le i-ème. C'est bien sûr pour cette raison que le
tout est si compliqué : je suis obligé de créer mon propre itérateur pour
tenir compte de cet index variable.
Par exemple, cela peut être :

double& GetValue(int i) {
return (*m_pMyTab)[random(m_pMyTab->size()-1];
}

Où random(int i) retourne un nombre choisi aléatoirement entre 0 et i.
J'aurais dû, alors, écrire directement la fonction GetValue dans la classe
itérateur MyIterator. Mais dans ce cas, je n'aurais pas pu en bénéficier
lors de la surcharge de [] dans la classe MyVector.


Avec un itérateur enregistrant un foncteur, on peut facilement
encapsuler des fonctions membres du conteneur, mais aussi créer de
tous nouveaux algorithmes d'itération, à l'extérieur de la classe.

Est-ce plus clair ?


Je pense. Mais peut-être quelque chose m'a échappé ... Notamment
la non visibilité du type de conteneur par l'itérateur.

Merci.


Attend de voir si la réponse te convient ;-).

--drkm



Avatar
Boris Sargos
Ta réponse me convient parfaitement. Je te remercie d'avoir passé du temps à
la rédiger, c'est vraiment très aimable de ta part.
En ce qui me concerne, ça me semble compliqué, mais je vais bien sûr le
tester.

Quelque chose m'a échappé ... Notamment
la non visibilité du type de conteneur par l'itérateur.


A cause des références (#include) croisées : MyVector connaît nécessairement
la définition de MyIterator parce que dans le fichier MyVector.h, j'ajoute
#include "MyIterator.h". Dans ce cas, je ne peux pas faire de même pour
MyIterator : je ne peux pas ajouter dans "MyIterator.h" la ligne "#include
MyVector.h". Et comme j'utilise des références et non pas des vecteurs comme
paramètres de mes fonctions, je ne peux pas utiliser la déclaration class
MyVector juste avant la définition de MyIterator.
De plus, il me semble qu'un itérateur doit être indépendant du conteneur,
n'est-ce pas ?

Juste une petite question, puisque tu sembles calé : as-tu déjà implémenté
une classe matrice en vue de calculs mathématiques performants ? Et dans ce
cas, si ce n'est pas trop long à décrire, quelle architecture Vector/Matrix
as-tu choisi ?

Je te remercie encore de tes réponses :-))
Bon dimanche.
Boris.

Avatar
drkm
"Boris Sargos" writes:

Ta réponse me convient parfaitement. Je te remercie d'avoir passé du temps à
la rédiger, c'est vraiment très aimable de ta part.


Pas de quoi.

En ce qui me concerne, ça me semble compliqué, mais je vais bien sûr le
tester.


Ce n'est pas si compliqué, une fois qu'on l'a compris :-). Mais
attend peut-être d'éventuelles critiques à son sujet. Je n'ai pas la
science infuse ...

Quelque chose m'a échappé ... Notamment
la non visibilité du type de conteneur par l'itérateur.


A cause des références (#include) croisées


// MyIterator.h

template< class Container , typename Accessor >
class MyIterator
{
 ...
} ;


// MyContainer.h

#include "MyIterator.h"

template< typename Type >
class MyContainer
{
public:
typedef ... MyAccessor ;
typedef MyIterator< MyContainer , MyAccessor >
Iterator ;
 ...
} ;

[...]

Juste une petite question, puisque tu sembles calé : as-tu déjà implémenté
une classe matrice en vue de calculs mathématiques performants ? Et dans ce
cas, si ce n'est pas trop long à décrire, quelle architecture Vector/Matrix
as-tu choisi ?


Moi, jamais. Mais je connais quelqu'un ;-). D'autres sont plus
adptes ici pour te répondre.

Je te remercie encore de tes réponses :-))


Merci ;-).

Bon dimanche.


Vous-mêmes.

--drkm