OVH Cloud OVH Cloud

Pointeur de fonction????

7 réponses
Avatar
Michael
Bonjour à tous,

j'ai la classe suivante, qui me sert à étaler dans le temps une
succession de requêtes à une base de données. (Tant qu'il y a des
requêtes dans liste_attente, le Timer tourne, sinon tout se met en pause)

class Attente
{
private:
TTimer * timer;

std::queue<AnsiString> liste_attente;

void __fastcall OnTimer(TObject *Sender);
public:
BDD_Queue();
void Add(const AnsiString & query);
void Pause() const;
void Resume() const;
};

Donc là c'est une utilisation très spécialisée, puisque je passe la
requête à Add, qui s'occupe du reste...

Comment je peux rendre cette classe plus générique, donc
Comment je peux faire pour appeller n'importe quelle fonction dans
Attente::Add()???

Par exemple dans le code j'aurais:

Attente->Add(BDD->Execute_Query(.....));

Mais je pourrais aussi faire:

Attente->Add(toto->OnSenFout(...));

J'espère avoir été clair!

Merci d'avance...

7 réponses

Avatar
Jean-Marc Bourguet
Michael writes:

Comment je peux rendre cette classe plus générique, donc
Comment je peux faire pour appeller n'importe quelle fonction dans
Attente::Add()???


Regarde les signaux. Il y en a chez boost, il y a aussi
libsig++ (utilisé par gtk--/gtkmm). Sinon avec google tu
devrais pouvoir retrouver des messages où c'est expliqué
comment faire; il me semble qu'il y en a un de moi.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Ahmed MOHAMED ALI
Bonjour,
Attente->Add(BDD->Execute_Query(.....));

Mais je pourrais aussi faire:

Attente->Add(toto->OnSenFout(...));


A moins que je n'ai pas compris ta problématique,tu n'as pas besoin de
pointeur de fonction là.
N'importe quel fonction (globale ou membre de classe) convient si elle
renvoie un AnsiString .

Ahmed MOHAMED ALI


"Michael" wrote in message
news:
Bonjour à tous,

j'ai la classe suivante, qui me sert à étaler dans le temps une
succession de requêtes à une base de données. (Tant qu'il y a des
requêtes dans liste_attente, le Timer tourne, sinon tout se met en pause)

class Attente
{
private:
TTimer * timer;

std::queue<AnsiString> liste_attente;

void __fastcall OnTimer(TObject *Sender);
public:
BDD_Queue();
void Add(const AnsiString & query);
void Pause() const;
void Resume() const;
};

Donc là c'est une utilisation très spécialisée, puisque je passe la
requête à Add, qui s'occupe du reste...

Comment je peux rendre cette classe plus générique, donc
Comment je peux faire pour appeller n'importe quelle fonction dans
Attente::Add()???

Par exemple dans le code j'aurais:

Attente->Add(BDD->Execute_Query(.....));

Mais je pourrais aussi faire:

Attente->Add(toto->OnSenFout(...));

J'espère avoir été clair!

Merci d'avance...


Avatar
Michael
"Ahmed MOHAMED ALI" wrote in
news:42543177$0$11698$:

Bonjour,
Attente->Add(BDD->Execute_Query(.....));

Mais je pourrais aussi faire:

Attente->Add(toto->OnSenFout(...));


A moins que je n'ai pas compris ta problématique,tu n'as pas besoin de
pointeur de fonction là.
N'importe quel fonction (globale ou membre de classe) convient si elle
renvoie un AnsiString .



là, Add(const AnsiString & query), c'est pour l'exemple...

je voudrais que Add(...) puisse pointer vers une fonction, peut-importe
laquelle, et que cette fonction soit exécutée quand le Timer le décide...


Avatar
kanze
Michael wrote:

j'ai la classe suivante, qui me sert à étaler dans le temps
une succession de requêtes à une base de données. (Tant qu'il
y a des requêtes dans liste_attente, le Timer tourne, sinon
tout se met en pause)

class Attente
{
private:
TTimer * timer;

std::queue<AnsiString> liste_attente;

void __fastcall OnTimer(TObject *Sender);
public:
BDD_Queue();
void Add(const AnsiString & query);
void Pause() const;
void Resume() const;
};

Donc là c'est une utilisation très spécialisée, puisque je
passe la requête à Add, qui s'occupe du reste...

Comment je peux rendre cette classe plus générique, donc
Comment je peux faire pour appeller n'importe quelle fonction
dans Attente::Add()???


La solution la plus simple, c'est d'utiliser un objet
fonctionnel polymorphique. Avec le polymorphisme dynamique, on
définit une classe de base, genre :

class Callback
{
public:
virtual ~Callback() {}
virtual void operator()(
std::string const& query ) = 0 ;
} ;

Ceci suppose une régistration par l'objet concerné pour
l'évenemment.

Par exemple dans le code j'aurais:

Attente->Add(BDD->Execute_Query(.....));

Mais je pourrais aussi faire:

Attente->Add(toto->OnSenFout(...));

J'espère avoir été clair!


Si on veut pouvoir appeler une fonction arbitraire, dans une
classe arbitraire, il faudrait se servir d'une intermédiaire
(modèle de conception adaptateur). Pour ça, on pourrait se
servir d'un template :

template< typename T >
class ConcreteCallback : public Callback
{
public:
explicit ConcreteCallback(
T* object,
void (T::*f)( std::string const& ) )
: myObject( object )
, myF( f ) ;
{
}

virtual void operator()(
std::string const& query )
{
(myObject->*f)( query ) ;
}

private:
T* myObject ;
void (T::* myF)( std::string const& ) ;
} ;

template< typename T >
std::auto_ptr< Callback >
getCallback( T* object, void (T::*f)( std::string const& ) )
{
return std::auto_ptr< T >(
new ConcreteCallback< T >( object, f ) ) ;
}

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Michael
template< typename T >
class ConcreteCallback : public Callback
{
public:
explicit ConcreteCallback(
T* object,
void (T::*f)( std::string const& ) )
: myObject( object )
, myF( f ) ;
{
}

virtual void operator()(
std::string const& query )
{
(myObject->*f)( query ) ;
}

private:
T* myObject ;
void (T::* myF)( std::string const& ) ;
} ;

template< typename T >
std::auto_ptr< Callback >
getCallback( T* object, void (T::*f)( std::string const& ) )
{
return std::auto_ptr< T >(
new ConcreteCallback< T >( object, f ) ) ;
}


Je suis désolé, mais je dois t'avouer que je ne comprends pas ton code, et
je ne vois pas comment je peux l'utiliser dans mon cas...

Avatar
Franck Branjonneau
Michael écrivait:

James


Tu as oublié la classe abstraite Callback.

template< typename T >
class ConcreteCallback : public Callback
{
} ;

template< typename T >
std::auto_ptr< Callback >
getCallback( T* object, void (T::*f)( std::string const& ) )
{
return std::auto_ptr< T >(
new ConcreteCallback< T >( object, f ) ) ;
}


Je suis désolé, mais je dois t'avouer que je ne comprends pas ton code, et
je ne vois pas comment je peux l'utiliser dans mon cas...


La fonction getCallback construit dans le tas un ConcreteCallback<>
que tu manipules avec un pointeur sur Callback. Pour s'assurer de sa
destruction, il est encapsulé dans un std::auto_ptr.

Tu n'en as, a priori, pas besoin si tu le manipules dans ta classe
Attente :

class Attente {
private:
// ...
std::queue<Callback *> liste_attente;

public:
// ...
template< typename T >
void
add(
T* object,
void (T::*f)(std::string const&)) {

liste_attente.push(new ConcreteCallback< T >(object, f));
}


void
use() {

(*liste_attente.front())("Something usefull");
liste_attente.pop();
}


~Attente()
throw() {

typedef std::queue<Callback *>::iterator Iterator;
Iterator const last(liste_attente.end());
for(
Iterator current(liste_attente.begin());
current != last;
++current) {

current->~Callback();
}
}
};


Tu l'utilises comme suit :

struct Foo {

void
bar(
std::string const & s) {

// Do something with s.
std::cout << s << "n";
}
};


// ...
Attente attente;

Foo foo;
attente.add(foo, &Foo::bar);

Et attente.use() execute foo.bar("Something usefull").
--
Franck Branjonneau


Avatar
James Kanze
Franck Branjonneau wrote:
Michael écrivait:


Tu as oublié la classe abstraite Callback.


Qui est évidemment la partie la plus importante. La reste, c'est
un exemple d'une façon (pas la seule) de s'en servir.

template< typename T >
class ConcreteCallback : public Callback
{
} ;




template< typename T >
std::auto_ptr< Callback >
getCallback( T* object, void (T::*f)( std::string const& ) )
{
return std::auto_ptr< T >(
new ConcreteCallback< T >( object, f ) ) ;
}




Je suis désolé, mais je dois t'avouer que je ne comprends pas
ton code, et je ne vois pas comment je peux l'utiliser dans
mon cas...



Il y a effectivement plusieurs « astuces » là-dedans. Rien
d'essentiel, mais des choses qui facilitent l'utilisation par la
suite, et font partie des idiomes du C++ moderne.

L'essentiel, c'est toujours que tu définis une interface
Callback, que le générateur d'évenemments appelle la fonction
virtuelle de l'interface, et que tu en dérives une classe qui
fait ce que tu veux. Puisque tu as parlé (vaguement) d'appeler
des fonctions membre d'un nom arbitraire dans une classe
arbitraire, j'ai proposé le template comme classe dérivée : tu
donnes la classe ciblée comme paramètre de template, et la
fonction membre comme paramètre du constructeur, et il fait la
reste. (On aurait aussi pu lui donner la fonction membre comme
paramètre de template.)

Mais enfin, c'est parfois pénible d'avoir à écrire
ConcreteCallback< MaClasse > partout. D'où la fonction templatée
getCallback. Parce qu'en C++, il y a une différence importante
entre les templates de classe et les templates de fonction : le
compilateur déduit automatiquement les paramètres (quand il
peut) des templates de fonction. Donc, à la place d'écrire :

new ConcreteCallback< MaClasse >( this, &MaClasse::maFonction )

on peut écrire :

getCallback( this, &MaClasse::maFonction ) ;

C'est une petite simplification. (Vraiment petite dans ce
cas-ci, parce qu'il faut nommer la classe quand même quand on
prend l'adresse de la fonction membre.)

La fonction getCallback construit dans le tas un
ConcreteCallback<> que tu manipules avec un pointeur sur
Callback. Pour s'assurer de sa destruction, il est encapsulé
dans un std::auto_ptr.


Tu n'en as, a priori, pas besoin si tu le manipules dans ta
classe Attente :


C'est effectivement une solution assez simple aussi. Déclarer le
ConcreteCallback comme membre. Dans bien des cas, même, la
solution la plus simple serait d'hériter directement de
Callback. Ces solutions sont en fait plus simple que la solution
que j'ai proposé, mais sont potentiellement moins souple. Telle
qu'on a écrire Callback, on ne pert pas beaucoup en lui faisant
un membre -- mais évidemment, on ne peut pas le faire avec des
classes déjà existantes, qu'on ne peut pas facilement modifier.
Dans une implémentation alternative de Callback, en revanche,
c'est le constructeur de la classe Callback qui fait la
régistration, et le destructeur qui fait la dérégistration ; une
chose de moins à laquelle l'utilisateur doit penser, mais du
coup, la durée de la régistration est liée à la durée de vie de
l'objet Callback, et il devient préférable de la séparer de la
durée de vie de l'objet concerné.

Et l'héritage direct introduit en plus la contrainte que la
classe ne peut implémenter qu'un seul callback.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34