OVH Cloud OVH Cloud

[débutant objet] tableau de pointeur de fonction

47 réponses
Avatar
cyrcocq
Bonjour,

Je définis une classe qui me permet de générer des formes d'ondes (extraits
code en fin de message(toutes critiques bienvenues ;-) ))
Dans cette classe, j'ai quelques fonctions,
de type
void F0 (double);

Je veux faire un tableau de pointeurs de fonctions mais je rencontre
quelques difficultés.

J'essaie de faire un
typedef void (*fptronde) (double);

puis je fais des

fonction[0]=&onde::F0;

Mais ça ne compile pas... error C2440: '=' : impossible de convertir de
'void (__thiscall onde::* )(double)' en 'onde::fptronde'

Allors 3 questions:

comment dois-je définir mon type de pointeur sur fonction???

suis-je obligé de passer par un type que je n'utilise qu'une fois?

Les données étant dupliquées pour chaque instance de la classe, que dois-je
faire pour n'avoir qu'une instance de ce pointeur de fonction?

Merci.






class onde
{
private:

double Amp, *PAmp, Freq, *PFreq, Deph, *PDeph, Off, *POff; //Amplitude
Frequence déphasage offset (numériques fixes) et pointeurs associés.

typedef void (*fptronde) (double);

fptronde fonction [5];

void sinusoide(double);
void carree (double);
void triangulaire (double);
void montante (double);
void descendante (double);

public:

int type; //type de générateur d'onde
double sortie;
bool zero;

onde();
double CalcSortie(double);
void SetAmplitude(double);
void LieAmplitude(double&);
void SetFrequence(double);
void LieFrequence(double&);
void SetDephasage(double);
void LieDephasage(double&);
void SetOffset(double);
void LieOffset(double&);
};

void onde::sinusoide(double t)
{
sortie= *PAmp * sin( *PFreq * t + *PDeph ) + *POff;
}
(...)
onde::onde()
{
Amp=Freq=Deph=Off=0;
PAmp=&Amp;
PFreq=&Freq;
PDeph=&Deph;
POff=&Off;
type=0;

fonction[0]=&onde::sinusoide;
fonction[1]=&onde::carree;
fonction[2]=&onde::triangulaire;
fonction[3]=&onde::montante;
fonction[4]=&onde::descendante;
}

double onde::CalcSortie(double t)
{
double temp=*PFreq * t;
(*(fonction[type]))(t);
}

void onde::SetAmplitude(double A)
{
Amp=A;
PAmp=&Amp;
}
void onde::LieAmplitude(double &A)
{
PAmp=&A;
}
(...)

10 réponses

1 2 3 4 5
Avatar
Stan
"cyrcocq" a écrit dans le message de news:
4332b923$0$304$
En fait pour tous mes paramétres j'ai une variable et un pointeur.
Au départ, je lie ce pointeur à mon paramétre.
Mais je peux aussi lier ce pointeur à un autre double défini par ailleurs.
C'est l'interet de cette méthode.
Par la suite, dans mes calcul d'ondes, je n'utilise pas ma variable Amp
(qui est définie uniquement pour le cas ou l'amplitude de mon onde ne
change pas), mais *PAmp (de maniére à pouvoir utiliser un paramétre ou une
sortie d'un autre objet générateur d'onde ou autre)

Ya une erreur dans mon raisonnement?



Ce genre d'inter-dépendance est souvent fatale...
Il faudrait décrire le fonctionnement général de ton programme...


--
-Stan

Avatar
Pascal Pizeine
"cyrcocq" a écrit dans le message de news:
4332c7ef$0$309$

"Pascal Pizeine" a écrit dans le message de
news: dgubqu$ga0$

En gros, j'ai un ensemble de générateurs d'ondes (éventuellement avec
des interconnections) et je peux changer la forme d'onde émise par
chacun de ces générateurs sans changer leurs interconncections...
Qu'en penses tu?


Tu pourrais avoir un objet générateur d'onde dont l'un des membre serait
une onde. Si tu changes de forme d'onde tu modifie cette variable.


Ca ressemble à ce que je tentais non?
Utiliser un indice permettant de choisir une fonction dans un tableau de
pointeur de fonctions.

Je pensait à une variable de type

onde *pOnde;

avec
pOnde = new sinusoide();

et si tu change d'onde
pOnde = new autreOnde();

Peut être que ce que tu fais convient aussi (Je n'y connais rien en onde et
en générateur d'onde).
La solution que je t'ai donné était une façon à mon avis plus objet de
définir une onde par rapport au code que tu avais posté.

En général une classe qui comporte une variable membre qui définit un type
et qui permet d'exécuter des fonctions (que ce soit par l'intermédiaire d'un
tableau de pointeur de fonction ou par un switch) peut être décomposée en
une classe générique avec des fonctions qui définissent le comportement
général et des classes dérivées qui permettent de spécialiser. C'est souvent
plus clair et plus facile à faire évoluer.

Pascal



Avatar
Fabien LE LEZ
On Thu, 22 Sep 2005 13:41:25 +0200, "cyrcocq" :

fptronde fonction [5];


Et un jour, tu ajouteras une fonction, et, ne te souvenant plus de la
signification (ou de l'existence) de ce "5", tu ne le modifieras
pas... et boum !

Je corrige donc cette faute de frappe :

std::vector <fptronde> fonction;

Autre souci : indexer ton tableau avec des entiers ne me paraît pas
adéquat. Que diable le "3" de "fonction[3]" ou de "type=3" peut-il
bien signifier ?

Il vaudrait mieux créer un type "Index", et mettre tes fonctions dans
un std::map<Index,type_fonction>.

Si c'est la logique interne du programme qui choisit la fonction,
Index sera un enum :

enum Index { FCT_carre, FCT_sinus, ... };

Si c'est l'utilisateur qui choisit la fonction, l'index pourra être le
texte affiché dans le menu, pour éviter les intermédiaires :

typedef std::string Index;

typedef void (*fptronde) (double);

comment dois-je définir mon type de pointeur sur fonction???


Comme un foncteur.

Note : la solution ci-dessous peut n'être pas optimale. L'exécution le
dira...



#include <map>

template <class Index, class Contenu> class MapPointeurs
{
// Je déteste l'interface de std::map<> et son operator[] non-const,
// donc je l'encapsule pour redéfinir l'interface
public:
void AjouteElement (Index const& index, Contenu* ptr)
{
assert (data.find (index) == data.end());
// L'élément n'existe pas déjà
data[index]= ptr;
}

Contenu const& operator[] (Index const& index)
{
Data::const_iterator it= data.find (index);
assert (it != data.end());
return *((*it).second);
}

private:
typedef typename std::map <Index, Contenu*> Data;
Data data;
};

/* Note : cette macro pourraît n'en être pas une (i.e. on met le code
directement dans la classe), mais je l'ai laissée telle quelle pour
des raisons de symétrie.
Son intérêt est dans la fonction virtuelle pure, qui permettra
d'accéder à ses classes dérivées depuis le tableau.
*/
#define MACROFONCTEURS_INITIALISATION
class BaseFoncteur
{ public: virtual void operator() (double) const= 0;
virtual ~BaseFoncteur(){} };
typedef MapPointeurs <IndexFoncteurs, BaseFoncteur> MapFoncteurs;
MapFoncteurs map_foncteurs;

/* La macro déclare la fonction en elle-même, et crée un objet
dérivant de BaseFoncteur, dont la seule utilité est d'appeler la
fonction. L'objet s'inscrit lui-même dans le tableau.
*/
#define MACROFONCTEURS_DECLARE_FONCTION(nom_fonction, index, classe)
void nom_fonction (double);
class FONCTEUR_##nom_fonction;
friend class FONCTEUR_##nom_fonction;
class FONCTEUR_##nom_fonction: public BaseFoncteur
{
public:
FONCTEUR_##nom_fonction (classe& objet_)
: objet (objet_)
{ objet.map_foncteurs.AjouteElement (index, this); }
void operator() (double x) const
{ return objet.nom_fonction (x); }
private:
classe& objet;
};
FONCTEUR_##nom_fonction objet_FONCTEUR_##nom_fonction;

/* Je n'ai pas trouvé le moyen d'éviter cette macro. D'ailleurs, si
quelqu'un le connaît, ça m'intéresse...
*/
#define MACROFONCTEURS_INTEGRATION_FONCTION( nom_fonction)
objet_FONCTEUR_##nom_fonction (*this)


class onde
{
private:
typedef std::string IndexFoncteurs;
MACROFONCTEURS_INITIALISATION

MACROFONCTEURS_DECLARE_FONCTION (sinusoide, "Sinusoïde", onde)
MACROFONCTEURS_DECLARE_FONCTION (carree, "Carrée", onde)


public:
onde();

void LancerFonction (IndexFoncteurs const& index, double x)
{ // Un exemple (assez bidon) d'utilisation de ce machin
map_foncteurs[index](x);
}
};

onde::onde ()
: MACROFONCTEURS_INTEGRATION_FONCTION (sinusoide)
, MACROFONCTEURS_INTEGRATION_FONCTION (carree)
/* Détail intéressant : si jamais tu oublies une de ces lignes, le
compilateur râlera -- "Can't find default constructor for..." */
{
}

void onde::sinusoide (double x)
{
// do something with
x;
}

void onde::carree (double x)
{
// do something with
x;
}

int main()
{
onde mon_onde;
mon_onde.LancerFonction ("Carrée", 12);
}

Avatar
Fabien LE LEZ
On Thu, 22 Sep 2005 18:23:24 +0200, Fabien LE LEZ
:

Son intérêt est dans la fonction virtuelle pure, qui permettra
d'accéder à ses classes dérivées depuis le tableau.


Phrase hautement bancale et syntaxiquement incorrecte, mais j'imagine
que tout le monde aura compris...

Avatar
Fabien LE LEZ
On Thu, 22 Sep 2005 18:23:24 +0200, Fabien LE LEZ
:

/* Note : cette macro pourraît


Mon dieu mon dieu...

En passant, si quelqu'un pouvait m'expliquer pourquoi je m'emmêle
systématiquement les pinceaux avec les accents circonflexes ces
temps-ci... Y a-t-il un psychanalyste dans la salle ?


J'en profite pour indiquer que si j'ai créé une classe "MapPointeurs"
plus ou moins bizarre, c'est parce qu'au départ, je pensais devoir
compter sur elle pour faire le ménage, avec un destructeur qui appelle
delete sur les pointeurs contenus.
Finalement, les pointeurs pointent vers des objets membres, donc le
compilo se charge lui-même du nettoyage.

Avatar
cyrcocq
"Stan" a écrit dans le message de news:
4332cba7$0$17231$

"cyrcocq" a écrit dans le message de news:
4332c7f0$0$309$

Toujours pas d'amélioration... J'ai pas trouvé une site sur lequel je
pourrais trouver la réponse à mon probléme. Là, j'ai vraiment besoin d'un

coup de main!



Je t'ai fait un petit exemple qui n'est pas aboutit
mais qui exploite l'opérateur ( ) qui peut être utile
dans ton cas.


Donc si je comprends bien (Stan et Fabien) l'approche tableau de pointeur de
fonction est a remplacer par une approche de foncteur.
Et bien je vais donc me pencher sur vos codes et voir si je comprends
tout...

Mais c'est frustrant de ne pas avoir réussi à utiliser le tableau de
pointeur de fonction!...



Avatar
cyrcocq
"Fabien LE LEZ" a écrit dans le message de news:

On Thu, 22 Sep 2005 13:41:25 +0200, "cyrcocq" :

fptronde fonction [5];


Et un jour, tu ajouteras une fonction, et, ne te souvenant plus de la
signification (ou de l'existence) de ce "5", tu ne le modifieras
pas... et boum !

Je corrige donc cette faute de frappe :

std::vector <fptronde> fonction;


Bon, je vois... ici ca rigole pas ;-)
Non, c'est chouette d'apprendre à faire et aussi d'apprendre à faire
corectement, mais j'avoue que là j'ai un peu peur de pas tout maîtriser.
Faut dire que je débute l'aspect objet, je ne sais pas si je peux déjà dire
que j'ai commencé la librairie standard...
J'ai l'impression de devoir tout apprendre d'un bloc.
Bref beaucoup de notions à assimiler!

Autre souci : indexer ton tableau avec des entiers ne me paraît pas
adéquat. Que diable le "3" de "fonction[3]" ou de "type=3" peut-il
bien signifier ?

Il vaudrait mieux créer un type "Index", et mettre tes fonctions dans
un std::map<Index,type_fonction>.

Si c'est la logique interne du programme qui choisit la fonction,
Index sera un enum :

enum Index { FCT_carre, FCT_sinus, ... };

Si c'est l'utilisateur qui choisit la fonction, l'index pourra être le
texte affiché dans le menu, pour éviter les intermédiaires :

typedef std::string Index;


Hmmm... Je vois... ou plutot, je vais réflechir à la question et je verrais!

typedef void (*fptronde) (double);

comment dois-je définir mon type de pointeur sur fonction???


Comme un foncteur.


J'ai bien l'impression que vous étes d'accord sur cet aspect! Tant pis pour
ma tentative d'assimilation progressive (basée sur la succession des
chapitres du cours de Christian Casteyde)

Decidemment, c'est frustrant de ne pas avoir trouvé/compris la méthode pour
utliser ce tableau de pointeur de fonction, il faut quand même que j'y
revienne...

typedef void (onde::*fptronde) (double);
// On est d'accord, je définis un type pointeur de fonction de ma classe
onde
static fptronde fonction [5];
// Je déclare un tableau de 5 de ces pointeurs
fonction[0]=&onde::sinusoide;
// J'initialise le premier pointeur de mon tableau
fonction[0] (t);
//Me dit:error C2064: le terme ne correspond pas à une fonction
//Pareil avec onde::fonction[type] (t); ou this->fonction[type] (t); ou
*fonction[0] (t);

Note : la solution ci-dessous peut n'être pas optimale. L'exécution le
dira...


Bon ok... Les foncteurs allors...
Bon j'imprimme ce code (et le code de Stan)
J'analyse un peu et je voie...


Merci







#include <map>

template <class Index, class Contenu> class MapPointeurs
{
// Je déteste l'interface de std::map<> et son operator[] non-const,
// donc je l'encapsule pour redéfinir l'interface
public:
void AjouteElement (Index const& index, Contenu* ptr)
{
assert (data.find (index) == data.end());
// L'élément n'existe pas déjà
data[index]= ptr;
}

Contenu const& operator[] (Index const& index)
{
Data::const_iterator it= data.find (index);
assert (it != data.end());
return *((*it).second);
}

private:
typedef typename std::map <Index, Contenu*> Data;
Data data;
};

/* Note : cette macro pourraît n'en être pas une (i.e. on met le code
directement dans la classe), mais je l'ai laissée telle quelle pour
des raisons de symétrie.
Son intérêt est dans la fonction virtuelle pure, qui permettra
d'accéder à ses classes dérivées depuis le tableau.
*/
#define MACROFONCTEURS_INITIALISATION
class BaseFoncteur
{ public: virtual void operator() (double) const= 0;
virtual ~BaseFoncteur(){} };
typedef MapPointeurs <IndexFoncteurs, BaseFoncteur> MapFoncteurs;
MapFoncteurs map_foncteurs;

/* La macro déclare la fonction en elle-même, et crée un objet
dérivant de BaseFoncteur, dont la seule utilité est d'appeler la
fonction. L'objet s'inscrit lui-même dans le tableau.
*/
#define MACROFONCTEURS_DECLARE_FONCTION(nom_fonction, index, classe)
void nom_fonction (double);
class FONCTEUR_##nom_fonction;
friend class FONCTEUR_##nom_fonction;
class FONCTEUR_##nom_fonction: public BaseFoncteur
{
public:
FONCTEUR_##nom_fonction (classe& objet_)
: objet (objet_)
{ objet.map_foncteurs.AjouteElement (index, this); }
void operator() (double x) const
{ return objet.nom_fonction (x); }
private:
classe& objet;
};
FONCTEUR_##nom_fonction objet_FONCTEUR_##nom_fonction;

/* Je n'ai pas trouvé le moyen d'éviter cette macro. D'ailleurs, si
quelqu'un le connaît, ça m'intéresse...
*/
#define MACROFONCTEURS_INTEGRATION_FONCTION( nom_fonction)
objet_FONCTEUR_##nom_fonction (*this)


class onde
{
private:
typedef std::string IndexFoncteurs;
MACROFONCTEURS_INITIALISATION

MACROFONCTEURS_DECLARE_FONCTION (sinusoide, "Sinusoïde", onde)
MACROFONCTEURS_DECLARE_FONCTION (carree, "Carrée", onde)


public:
onde();

void LancerFonction (IndexFoncteurs const& index, double x)
{ // Un exemple (assez bidon) d'utilisation de ce machin
map_foncteurs[index](x);
}
};

onde::onde ()
: MACROFONCTEURS_INTEGRATION_FONCTION (sinusoide)
, MACROFONCTEURS_INTEGRATION_FONCTION (carree)
/* Détail intéressant : si jamais tu oublies une de ces lignes, le
compilateur râlera -- "Can't find default constructor for..." */
{
}

void onde::sinusoide (double x)
{
// do something with
x;
}

void onde::carree (double x)
{
// do something with
x;
}

int main()
{
onde mon_onde;
mon_onde.LancerFonction ("Carrée", 12);
}










Avatar
Fred
Bon, en m'aidant un peu de google, j'ai trouvé la syntaxe pour appeler
les pointeurs de méthode. Ca compile, par contre, je ne garantis pas que
ça marche :)


class wave
{
public:
typedef void (wave::*fptronde) (double);

void sinusoide(double)
{}

static fptronde fonction [5];

static init(wave & w)
{
fonction[0] = &wave::sinusoide;
// La syntaxe qui tue
((&w)->*(fonction[0]))(5);
}
};
Avatar
Fabien LE LEZ
On Thu, 22 Sep 2005 21:34:03 +0200, "cyrcocq" :

Donc si je comprends bien (Stan et Fabien) l'approche tableau de pointeur de
fonction est a remplacer par une approche de foncteur.


Oui. En rappelant que le type canonique pour un tableau en C++ est
std::vector<>.

Mais c'est frustrant de ne pas avoir réussi à utiliser le tableau de
pointeur de fonction!...


Je trouve rassurant, au contraire, qu'une solution fiable, élégante et
extensible soit celle qui fonctionne :-)

Au fait, un petit point de détail : les "foncteurs" dont on parle ici
ne fonctionneront pas correctement avec les algorithmes de la STL
(std::find_if par exemple), car ces derniers prennent leurs arguments
par valeur[*] (et les fonctions virtuelles ne le sont plus[**]).

[*] Pourquoi, au fait ?

[**] Oui, je sais, c'est un abus de langage.

Avatar
Fabien LE LEZ
On Thu, 22 Sep 2005 22:09:15 +0200, "cyrcocq" :

std::vector <fptronde> fonction;


Bon, je vois... ici ca rigole pas ;-)


D'un autre côté, grâce à std::vector<>, on peut se laisser aller -- il
est rigoureux à notre place ;-)

Faut dire que je débute l'aspect objet, je ne sais pas si je peux déjà dire
que j'ai commencé la librairie standard...


Tu prends les choses à l'envers. Commence par t'assurer que tu sais
utiliser std::string et std::vector<>. Ensuite seulement, occupe-toi
de créer tes propres classes.

J'ai bien l'impression que vous étes d'accord sur cet aspect! Tant pis pour
ma tentative d'assimilation progressive


Étant moi-même passé progressivement du C au C++, je peux te garantir
que c'est une grave erreur, qui fait perdre beaucoup de temps. Mieux
vaut oublier une bonne partie du C et commencer le C++ sur de bonnes
bases.

Decidemment, c'est frustrant de ne pas avoir trouvé/compris la méthode pour
utliser ce tableau de pointeur de fonction


Tu essaies d'appliquer des méthodes du langage C à des objets du
langage C++. Ce n'est pas étonnant que ça coince.

typedef void (onde::*fptronde) (double);


Je ne sais pas si c'est la bonne syntaxe.

En fait, quand je dois passer un pointeur de fonction (à une
bibliothèque écrite en C), je passe par une fonction libre ou
statique, qui a une "vraie" adresse :

class C
{
static void static_foo (C* ptr_this, autres paramètres)
{ ptr_this-> foo (autres paramètres); }

void foo (autres paramètres);

void bar()
{
appeler_fonction_en_C (static_foo, autres paramètres dont this);
}
};


fonction[0]=&onde::sinusoide;


Note que l'opérateur & est inutile pour les fonctions.

// J'initialise le premier pointeur de mon tableau
fonction[0] (t);

[snip quelques dizaines de lignes de code]


Inutile de citer l'intégralité d'un message pour rien. En fait, il est
généralement inutile de citer quoi que ce soit après la dernière ligne
de ton propre message. Merci d'avance.


1 2 3 4 5