OVH Cloud OVH Cloud

Qn sur les pointeurs de fonction dérivées de templates

7 réponses
Avatar
pocky6
Bonjour à tous,
Voilà ma qn:
J'ai une classe du genre
//Array2D.h
template <class TCell> class Array2D<TCell> {
...
public:
TCell GetAt1(int x, int y) {...};
TCell GetAt2(int x, int y) {...};
TCell GetAt3(int x, int y) {...};
TCell (*GetAt)(int, int);
}

//Image.h
class Image:public Array2D<Couleur> {...}

GetAt est censée pouvoir choisir de prendre une "valeur" parmi GetAt1,
GetAt2 et GetAt3, directement, de manière à éviter une sélection entre
les méthodes (vu qu'elle est appelée souvent...). Ayant échoué à
l'initialiser dans le constructeur de Array2D (GetAt=GetAt1; ou
GetAt=&GetAt1; me donnent des erreurs) j'ai tenté de rajouter :
void SelectFunc(TCell (*f)(int, int)) { GetAt=f; } (avec ou sans &)
qui compile bien jusqu'à ce que je l'appelle qq part (constructeur de
Array2D ou de Image), dans Image par ex. parce qu'il ne trouve pas de
fonction correspondant à GetAt1 (passée en paramètre) alors qu'elle
est définie dans la classe héritée.

J'utilise gcc3.2 (si ça change qqch...)

Merci de m'aider en me disant si je fais fausse route, ce qui cloche,
ou sinon comment faire.

7 réponses

Avatar
Franck Branjonneau
(Etienne Lemieux) écrivait:

J'ai une classe du genre
//Array2D.h
template <class TCell> class Array2D<TCell> {
....
public:
TCell GetAt1(int x, int y) {...};
TCell GetAt2(int x, int y) {...};
TCell GetAt3(int x, int y) {...};
TCell (*GetAt)(int, int);
}

//Image.h
class Image:public Array2D<Couleur> {...}

GetAt est censée pouvoir choisir de prendre une "valeur" parmi GetAt1,
GetAt2 et GetAt3, directement, de manière à éviter une sélection entre
les méthodes (vu qu'elle est appelée souvent...). Ayant échoué à
l'initialiser dans le constructeur de Array2D (GetAt=GetAt1; ou
GetAt=&GetAt1; me donnent des erreurs) j'ai tenté de rajouter :
void SelectFunc(TCell (*f)(int, int)) { GetAt=f; } (avec ou sans &)
qui compile bien jusqu'à ce que je l'appelle qq part (constructeur de
Array2D ou de Image), dans Image par ex. parce qu'il ne trouve pas de
fonction correspondant à GetAt1 (passée en paramètre) alors qu'elle
est définie dans la classe héritée.


Le C++ distingue deux types de pointeurs : les pointeurs et les
pointeurs sur membres (non statiques). C'est les seconds qu'il te faut
puisque GetA1, GetA2 et GetA3 sont des fonctions.

Tu écris donc :

TCell (Array2D::*GetAt)(int, int);

Un tel pointeur nécessite un objet pour être déréfencé (son this) :

Array2D a;
a.*GetAt(0, 0)
--
Franck Branjonneau

Avatar
Etienne Lemieux
"Franck Branjonneau" a écrit dans le message de
news:
(Etienne Lemieux) écrivait:

J'ai une classe du genre
//Array2D.h
template <class TCell> class Array2D<TCell> {
....
public:
TCell GetAt1(int x, int y) {...};
TCell GetAt2(int x, int y) {...};
TCell GetAt3(int x, int y) {...};
TCell (*GetAt)(int, int);
}

//Image.h
class Image:public Array2D<Couleur> {...}

GetAt est censée pouvoir choisir de prendre une "valeur" parmi GetAt1,
GetAt2 et GetAt3, directement, de manière à éviter une sélection entre
les méthodes (vu qu'elle est appelée souvent...). Ayant échoué à
l'initialiser dans le constructeur de Array2D (GetAt=GetAt1; ou
GetAt=&GetAt1; me donnent des erreurs) j'ai tenté de rajouter :
void SelectFunc(TCell (*f)(int, int)) { GetAt=f; } (avec ou sans &)
qui compile bien jusqu'à ce que je l'appelle qq part (constructeur de
Array2D ou de Image), dans Image par ex. parce qu'il ne trouve pas de
fonction correspondant à GetAt1 (passée en paramètre) alors qu'elle
est définie dans la classe héritée.


Le C++ distingue deux types de pointeurs : les pointeurs et les
pointeurs sur membres (non statiques). C'est les seconds qu'il te faut
puisque GetA1, GetA2 et GetA3 sont des fonctions.

Tu écris donc :

TCell (Array2D::*GetAt)(int, int);

Un tel pointeur nécessite un objet pour être déréfencé (son this) :

Array2D a;
a.*GetAt(0, 0)
--
Franck Branjonneau


Merci pour l'indication mais je n'arrive pas à venir à bout des erreurs à la
compil. J'ai essayé de rajouter Array2D<TCell>:: à la déclaration comme
indiqué, ça entraîne l'impossibilité de retrouver depuis l'appel dans le
constructeur de Image la fonction SelectFunc.
Seconde conséquence, dans une fonction membre de Image j'ai droit à "must
use .* or -> to call pointer-to-member function" ce qui semble être bon
signe mais quoi que j'essaie (this.*GetAt, this->GetAt, etc) rien n'y fait
l'erreur reste. Array2D étant la classe mère de Image, je ne peux en
déclarer une instance au pif comme ça....
Cmt m'en sortir ?


Avatar
Franck Branjonneau
"Etienne Lemieux" écrivait:

"Franck Branjonneau" a écrit dans le message de
news:
(Etienne Lemieux) écrivait:

J'ai une classe du genre
//Array2D.h
template <class TCell> class Array2D<TCell> {
....
public:
TCell GetAt1(int x, int y) {...};
TCell GetAt2(int x, int y) {...};
TCell GetAt3(int x, int y) {...};
TCell (*GetAt)(int, int);
}

//Image.h
class Image:public Array2D<Couleur> {...}

GetAt est censée pouvoir choisir de prendre une "valeur" parmi GetAt1,
GetAt2 et GetAt3, directement, de manière à éviter une sélection entre
les méthodes (vu qu'elle est appelée souvent...). Ayant échoué à
l'initialiser dans le constructeur de Array2D (GetAt=GetAt1; ou
GetAt=&GetAt1; me donnent des erreurs) j'ai tenté de rajouter :
void SelectFunc(TCell (*f)(int, int)) { GetAt=f; } (avec ou sans &)
qui compile bien jusqu'à ce que je l'appelle qq part (constructeur de
Array2D ou de Image), dans Image par ex. parce qu'il ne trouve pas de
fonction correspondant à GetAt1 (passée en paramètre) alors qu'elle
est définie dans la classe héritée.


Le C++ distingue deux types de pointeurs : les pointeurs et les
pointeurs sur membres (non statiques). C'est les seconds qu'il te faut
puisque GetA1, GetA2 et GetA3 sont des fonctions.

Tu écris donc :

TCell (Array2D::*GetAt)(int, int);

Un tel pointeur nécessite un objet pour être déréfencé (son this) :

Array2D a;
a.*GetAt(0, 0)



[ merci de ne pas citer les signatures. ]

Merci pour l'indication mais je n'arrive pas à venir à bout des erreurs à la
compil. J'ai essayé de rajouter Array2D<TCell>:: à la déclaration comme
indiqué, ça entraîne l'impossibilité de retrouver depuis l'appel dans le
constructeur de Image la fonction SelectFunc.


Tu est trop flou. Extrait de code ?

Seconde conséquence, dans une fonction membre de Image j'ai droit à "must
use .* or -> to call pointer-to-member function" ce qui semble être bon
signe mais quoi que j'essaie (this.*GetAt, this->GetAt, etc) rien n'y fait
l'erreur reste.


Si p est un pointeur sur Array2D< TCell >, (p->*GetAt)().

Array2D étant la classe mère de Image, je ne peux en déclarer une
instance au pif comme ça.... Cmt m'en sortir ?


Utiliser des fonctions virtuelles ? des spécialisations partielles ?
--
Franck Branjonneau



Avatar
Anthony Fleury
Etienne Lemieux wrote:

Merci pour l'indication mais je n'arrive pas à venir à bout des erreurs à
la compil. J'ai essayé de rajouter Array2D<TCell>:: à la déclaration comme
indiqué, ça entraîne l'impossibilité de retrouver depuis l'appel dans le
constructeur de Image la fonction SelectFunc.
Seconde conséquence, dans une fonction membre de Image j'ai droit à "must
use .* or -> to call pointer-to-member function" ce qui semble être bon
signe mais quoi que j'essaie (this.*GetAt, this->GetAt, etc) rien n'y fait
l'erreur reste. Array2D étant la classe mère de Image, je ne peux en
déclarer une instance au pif comme ça....
Cmt m'en sortir ?


Bon, je ne suis pas sûr d'avoir compris le problème, mais je vais tenter...
Au passage la notation est lourde et la solution choisi me parait assez
bizarre mais ca se fait.

template <class TCell> class Array2D {
public:
Array2D(TCell (Array2D::*GetAt)(int,int)):GetAt(GetAt) {}

// Le constructeur de Array2D recoit un pointeur vers une fonction membre
qui renvoie un TCell et recoit deux entiers.

TCell GetAt1(int x, int y) { }
TCell GetAt2(int x, int y) { }
TCell GetAt3(int x, int y) { }
TCell (Array2D::*GetAt)(int, int);
virtual ~Array2D() {}
};

template <class T> class Image:public Array2D<T> {
public:
Image(T (Array2D<T>::*f)(int,int))
: Array2D<T>(f)
// De même le constructeur de Image recoit un pointeur vers une fonction
membre comme au dessus, et construit la classe dont elle hérite avec ce
pointeur.
{
}
};

Le moyen ensuite de construire tes différents types :
Image<int> I1(&Array2D<int>::GetAt1);
Image<int> I2(&Array2D<int>::GetAt2);
Image<int> I3(&Array2D<int>::GetAt3);
Array2D<int> A(&Array2D<int>::GetAt1);

Ensuite pour appeler ta fonction je n'ai trouvé que :

int (Array2D<int>::*G)(int,int) = I1.GetAt;
(I1.*G)(1,2);

Tout autre type d'appel échoue car est fait en dehors de la classe.

Y-a-t-il une autre solution ? directement sans passer par un pointeur
intermédiaire.

Anthony
--
"I should have seen it would be this way
I should have known from the start what she's up to
When you have loved and you've lost someone
You know what it feels like to lose" -- The Rasmus

Avatar
Etienne Lemieux
"Etienne Lemieux" a écrit dans le message de
news:c2erog$1pu$
"Franck Branjonneau" a écrit dans le message de
news:
(Etienne Lemieux) écrivait:

J'ai une classe du genre
//Array2D.h
template <class TCell> class Array2D<TCell> {
....
public:
TCell GetAt1(int x, int y) {...};
TCell GetAt2(int x, int y) {...};
TCell GetAt3(int x, int y) {...};
TCell (*GetAt)(int, int);
}

//Image.h
class Image:public Array2D<Couleur> {...}

GetAt est censée pouvoir choisir de prendre une "valeur" parmi GetAt1,
GetAt2 et GetAt3, directement, de manière à éviter une sélection entre
les méthodes (vu qu'elle est appelée souvent...). Ayant échoué à
l'initialiser dans le constructeur de Array2D (GetAt=GetAt1; ou
GetAt=&GetAt1; me donnent des erreurs) j'ai tenté de rajouter :
void SelectFunc(TCell (*f)(int, int)) { GetAt=f; } (avec ou sans &)
qui compile bien jusqu'à ce que je l'appelle qq part (constructeur de
Array2D ou de Image), dans Image par ex. parce qu'il ne trouve pas de
fonction correspondant à GetAt1 (passée en paramètre) alors qu'elle
est définie dans la classe héritée.


Le C++ distingue deux types de pointeurs : les pointeurs et les
pointeurs sur membres (non statiques). C'est les seconds qu'il te faut
puisque GetA1, GetA2 et GetA3 sont des fonctions.

Tu écris donc :

TCell (Array2D::*GetAt)(int, int);

Un tel pointeur nécessite un objet pour être déréfencé (son this) :

Array2D a;
a.*GetAt(0, 0)



Je vais essayer d'être plus clair (après avoir lu les réponses de Franck et
Anthony):
Array2D est une template class qui sert surtout d'usine à classes, elle
n'est jamais instanciée directement (du moins c pas prévu). Le constructeur
est de la forme :
Array2D<TCell>(int width, int height):w(width), h(height), X {
X;
}
avec soit GetAt(GetAt1) à la place du premier X, soit une initialisation à
la place du deuxieme, si c'est possible. GetAt n'est pas constante mais a
une valeur par défaut prédéterminée par moi (cad GetAt1). Je ne veux donc
pas le faire passer en paramètre du constructeur.
GetAt est donc du type TCell (*GetAt)(int, int); et tous les GetAti prennent
deux int et renvoient un TCell (en fait les GetAti sont autant
d'"observateurs" avec des méthodes d'interpolation différentes, et sont
définies DANS Array2D. Elles devraient donc faire partie des méthodes de
toutes les classes dérivées, tout comme GetAt.
La fonction qui prend en paramètre GetAt2 et doit dire à GetAt de pointer
vers elle, bloque à la compil, d'où que je l'appelle. (j'ai essayé plein
d'écritures). En général elle doit être appelable en interne au moins par
les classes dérivées. Là mon instance de Image ne fait qu'appeler des
méthodes qui elles mêmes tentent d'appeler SelectFunc, et d'utiliser le
GetAt ainsi modifié.

Je crois n'avoir rien oublié là.
Je peux éventuellement copier un bout de code (sans les parties qui n'ont
rien à voir) mais il n'y aurait rien de plus que ce que j'ai copié déjà...



Avatar
Anthony Fleury
Etienne Lemieux wrote:

Je vais essayer d'être plus clair (après avoir lu les réponses de Franck
et Anthony):
Array2D est une template class qui sert surtout d'usine à classes, elle
n'est jamais instanciée directement (du moins c pas prévu). Le
constructeur est de la forme :
Array2D<TCell>(int width, int height):w(width), h(height), X {
X;
}
avec soit GetAt(GetAt1) à la place du premier X, soit une initialisation à
la place du deuxieme, si c'est possible. GetAt n'est pas constante mais a
une valeur par défaut prédéterminée par moi (cad GetAt1). Je ne veux donc
pas le faire passer en paramètre du constructeur.


Bon bon, en espérant que j'ai bien compris :

C'est la réponse que j'ai donnée, mais avec une petite modification :

Array2D(int width, int height):w(width), h(height), GetAt(&Array2D::GetAt1)
{ }

ou

Array2D(int width, int height):w(width), h(height) {
GetAt = &Array2D::GetAt1;
}

Tu n'as pas besoin que ton X soit un paramètre pour qu'il soit construit
dans une liste d'initialisation.

GetAt est donc du type TCell (*GetAt)(int, int); et tous les GetAti
prennent deux int et renvoient un TCell


Non non, comme l'a dit Franck Branjonneau des pointeurs de fonction et des
pointeurs de fonctions membres sont différents. Le pointeur de fonction
membre dépend de l'instanciation de la classe, qui a son this.
Il est donc du type :

TCell (Array2D::*GetAt)(int,int)

La fonction qui prend en paramètre GetAt2 et doit dire à GetAt de pointer
vers elle, bloque à la compil, d'où que je l'appelle. (j'ai essayé plein
d'écritures). En général elle doit être appelable en interne au moins par
les classes dérivées. Là mon instance de Image ne fait qu'appeler des
méthodes qui elles mêmes tentent d'appeler SelectFunc, et d'utiliser le
GetAt ainsi modifié.


Une telle fonction s'écrirait :

void SelectFunc(TCell (Array2D::*GetAt)(int, int)) { this->GetAt = GetAt; }

Ensuite comme tu es dans ta classe, tu peux appeler la fonction pointée
par :
(this->*GetAt)(x,y);

Petit exemple pour être un peu plus clair :

#include <iostream>
using std::cout;
using std::endl;

template <class TCell> class Array2D {
int w,h;
public:
Array2D(int width, int height):w(width), h(height),
GetAt(&Array2D::GetAt1) { }
// Le constructeur initialise GetAt avec la valeur par défaut

TCell GetAt1(int x, int y) { cout << "1" << endl; }
// Bon pour l'exemple faut différencier les trois
TCell GetAt2(int x, int y) { cout << "2" << endl; }
TCell GetAt3(int x, int y) { cout << "3" << endl; }
TCell (Array2D::*GetAt)(int, int);
void SelectFunc(TCell (Array2D::*GetAt)(int, int)) { this->GetAt GetAt; }

// La fonction qui change GetAt. Elle recoit un pointeur sur une fonction
membre et change this->GetAt.

virtual ~Array2D() {}
};

template <class T> class Image:public Array2D<T> {
public:
Image(int w = 0, int h = 0)
: Array2D<T>(w,h)
// Construction de la classe parent
{
}
void methode(int a);
// La méthode qui permet les changements par appel à SelectFunc et les
appels.
};

template <class T> void Image<T>::methode(int a) {
switch(a) {
case 1:
SelectFunc(&Array2D<T>::GetAt1);
// Appel de SelectFunc avec un pointeur valide sur la bonne fonction.
break;
case 2:
SelectFunc(&Array2D<T>::GetAt2);
// Pareil pour les deux autres
break;
case 3:
SelectFunc(&Array2D<T>::GetAt3);
break;
}
(this->*GetAt)(1,2);

// Appel de ta fonction membre.
}

// Pour l'exemple :
int main() {
Image<int> I;
I.methode(1);
I.methode(2);
I.methode(3);
}

Sortie du programme :
:~$ g++ test.cpp
:~$ ./a.out
1
2
3
:~$

En esperant que c'est clair et que ca réponde au problème.

Anthony
--
"I should have seen it would be this way
I should have known from the start what she's up to
When you have loved and you've lost someone
You know what it feels like to lose" -- The Rasmus

Avatar
Etienne Lemieux
GetAt est donc du type TCell (*GetAt)(int, int); et tous les GetAti
prennent deux int et renvoient un TCell
Non non, comme l'a dit Franck Branjonneau des pointeurs de fonction et des

pointeurs de fonctions membres sont différents. Le pointeur de fonction
membre dépend de l'instanciation de la classe, qui a son this.
Il est donc du type :
TCell (Array2D::*GetAt)(int,int)
Oups ça c'était une faute d'inattention. Faut dire je suis pas très à l'aise

sur
la nuance des fonctions membres (ça se voit...) par rapport aux fonctions
style C.

Ensuite comme tu es dans ta classe, tu peux appeler la fonction pointée
par :
(this->*GetAt)(x,y);
là j'avoue qu'il faudra que j'en trouve plus sur les pointeurs de fonction

membres,
je n'aurais pas pensé à cette notation du tout. (surtout les parenthèses
autour).
Maintenant J'ai encore une petite question: je fais surtout ça pour
expérimenter des choses en
c++, je suis donc libre de changer la structure du programme (enfin limité
par mes
connaissances surtout...). Et là si je résume, ce à quoi je voulais aboutir,
remplacer
un switch ou un tableau de fonctions par un pointeur simple, se transforme
en qqch
de moins simple. Existe-t-il une formule moins inspirée du C et plus "objet"
ou prévue
pour simplifier ce genre de cas ?
"Utiliser des fonctions virtuelles ? des spécialisations partielles ?"
(dixit Franck) est-ce
+ adapté ? (je ne connais pas les spécialisations...).

Petit exemple pour être un peu plus clair :
Merci bcp, j'ai trouvé tout ce dont j'avais besoin dans ce "petit" exemple

(très complet).

En esperant que c'est clair et que ca réponde au problème.
ça le résout oui :) Me reste à approfondir le sujet maintenant.

Merci encore.