Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

comment déclarer une fonction membre comme fonction callback ?

13 réponses
Avatar
Eric Bart
Bonjour,

J'aimerais utiliser une fonction membre d'une classe comme une fonction
callback.

Par exemple pour utiliser une thread :
pthread_create (&th, NULL, MaClasse::MaFonction, NULL);

Si MaFonction est déclarée en static, ça fonctionne.

Mais est-ce possible avec une fonction membre non statique ?

Il faudrait pouvoir donner dynamiquement l'adresse de la fonction ... non ?

Merci

10 réponses

1 2
Avatar
Loïc Joly
Eric Bart wrote:

Bonjour,

J'aimerais utiliser une fonction membre d'une classe comme une fonction
callback.

Par exemple pour utiliser une thread :
pthread_create (&th, NULL, MaClasse::MaFonction, NULL);

Si MaFonction est déclarée en static, ça fonctionne.

Mais est-ce possible avec une fonction membre non statique ?


Non.

Il faudrait pouvoir donner dynamiquement l'adresse de la fonction ... non ?
??


La technique classique la suivante (je détaille pas trop, c'est souvent
posé ici, regarde google pour plus de détails).

void threadFct (void* data)
{
MaClasse *mc = static_cast<MaClasse*> data;
mc->MaFonction();
}

int f()
{
MaClasse c;
// ...
pthread_create (&th, NULL, &threadFct, &c);
}

--
Loîc

Avatar
Falk Tannhäuser
Loïc Joly wrote:
Eric Bart wrote:

Bonjour,

J'aimerais utiliser une fonction membre d'une classe comme une fonction
callback.

Par exemple pour utiliser une thread :
pthread_create (&th, NULL, MaClasse::MaFonction, NULL);

Si MaFonction est déclarée en static, ça fonctionne.



Même ça n'est pas garanti, car à mon avis, pthread_create nécessite
comme argument un pointeur sur une fonction qualifiée 'extern "C"'.
Les fonctions C++ et C ne sont pas forcement interchangeables entre
elles, non seulement à cause du "name mangling" (qui n'intervient
pas lorsque l'appel se fait par pointeur) mais aussi parce que
la "calling convention" (Passage des paramètres dans des registres
ou sur la pile ? Dans quel ordre ? Qui fait le ménage sur la pile
au retour - la fonction appelée ou la fonction appelante ? Comment
passer la valeur de retour ?) peut différer entre les deux langages.
Certains compilateurs (par exemple GCC) acceptent la conversion
sans problème (soit ils utilisent la même convention d'appel, soit
ils génèrent un bout de code faisant les conversions nécessaires
s'il en y a besoin) mais cela reste non portable.

(À mon avis, ça serait *Bien* si une nouvelle release de la Norme
C++ mandatait un comportement satisfaisant dans tous les cas...)

Mais est-ce possible avec une fonction membre non statique ?


Non.

Il faudrait pouvoir donner dynamiquement l'adresse de la fonction ...
non ?


??

La technique classique la suivante (je détaille pas trop, c'est souvent
posé ici, regarde google pour plus de détails).


extern "C" // pour faire plus beau
void threadFct (void* data)
{
MaClasse *mc = static_cast<MaClasse*> data;
mc->MaFonction();
}

int f()
{
static

MaClasse c;
// ...
pthread_create (&th, NULL, &threadFct, &c);
}


Là, il faut faire attention à ce que la durée de vie de l'objet 'c'
ne soit pas plus courte que celle du (de la ?) "thread" crée(e) !
Donc il faudrait déclarer 'c' comme 'static' ou global (attention
alors à la création possible de plusieurs threads qui accèdent
au même objet), ou bien créer un objet dynamique qui ne devra être
détruit qu'une fois quand l[ea] thread en question est terminé(e).

Falk


Avatar
drkm
"Eric Bart" writes:

Il faudrait pouvoir donner dynamiquement l'adresse de la fonction


Si par « dynamiquement » tu entends « à l'exécution », c'est bien le
cas.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html

Avatar
Eric Bart
Mais est-ce possible avec une fonction membre non statique ?


Non.


OK. Merci


Avatar
Eric Bart
"drkm" wrote in message news:
"Eric Bart" writes:

Il faudrait pouvoir donner dynamiquement l'adresse de la fonction


Si par « dynamiquement » tu entends « à l'exécution », c'est bien le
cas.


Oui c'est ce que je voulais dire.

Mon idée était mauvaise, je pensais pouvoir convertir l'adresse d'une fonction
membre en une adresse de fonction callback. Ca ne doit pas être possible ...


Avatar
drkm
"Eric Bart" writes:

Mon idée était mauvaise, je pensais pouvoir convertir l'adresse
d'une fonction membre en une adresse de fonction callback. Ca ne
doit pas être possible ...


Recherche dans les archives du groupe après quelque chose comme
"extern C callbak foncteur", tu devrais trouver pas mal de solutions.
C'est une question récurrente.

L'idée est que si le pointeur sur fonction doit pointer sur une
fonction extern "C", on n'a pas le choix, on doit créer une fonction
de ce type. Mais elle peut s'arranger pour déléguer à une classe
(fonction membre statique) ou un objet (fonction membre d'instance).

Souvent, les fonctions acceptant des callbacks C acceptent également
un pointeur sur void. On passe alors l'adresse d'un objet. La
fonction destinée à être enregistrée caste alors son argument vers le
type ad-hoc, et appelle la fonction membre ad-hoc de cette objet.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html

Avatar
kanze
Falk Tannhäuser wrote in message
news:<cgkrae$p73$...
Loïc Joly wrote:
Eric Bart wrote:

J'aimerais utiliser une fonction membre d'une classe comme une
fonction callback.

Par exemple pour utiliser une thread :
pthread_create (&th, NULL, MaClasse::MaFonction, NULL);

Si MaFonction est déclarée en static, ça fonctionne.



Même ça n'est pas garanti, car à mon avis, pthread_create nécessite
comme argument un pointeur sur une fonction qualifiée 'extern "C"'.


Tout à fait.

Les fonctions C++ et C ne sont pas forcement interchangeables entre
elles, non seulement à cause du "name mangling" (qui n'intervient pas
lorsque l'appel se fait par pointeur) mais aussi parce que la "calling
convention" (Passage des paramètres dans des registres ou sur la pile
? Dans quel ordre ? Qui fait le ménage sur la pile au retour - la
fonction appelée ou la fonction appelante ? Comment passer la valeur
de retour ?) peut différer entre les deux langages. Certains
compilateurs (par exemple GCC) acceptent la conversion sans problème
(soit ils utilisent la même convention d'appel, soit ils génèrent un
bout de code faisant les conversions nécessaires s'il en y a besoin)
mais cela reste non portable.


Accepter le code, c'est une extension non conforme, parce que la norme
exige un diagnostique dans ce cas-ci.

(À mon avis, ça serait *Bien* si une nouvelle release de la Norme C++
mandatait un comportement satisfaisant dans tous les cas...)


Le problème, c'est que la norme C++ ne peut pas imposer des contraints
sur le C. Le but de l'« extern "Langage" », c'est justement de dire au
compilateur qu'il faut utiliser d'autres conventions. Je ne vois pas
comment la norme C++ pourrait imposer que les conventions soient les
même en C et en C++ -- il ne peut qu'imposer les contraints sur le C++.

Dans la pratique, il faut dire aussi qu'il y a eu des implémentations où
les conventions C et C++ différaient. Ce n'est donc pas qu'une
considération théorique.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
kanze
"Eric Bart" wrote in message
news:<412ddfc3$0$23228$...

J'aimerais utiliser une fonction membre d'une classe comme une
fonction callback.

Par exemple pour utiliser une thread :
pthread_create (&th, NULL, MaClasse::MaFonction, NULL);

Si MaFonction est déclarée en static, ça fonctionne.

Mais est-ce possible avec une fonction membre non statique ?


Ce n'est pas possible avec une fonction membre, statique ou non. Il faut
que ce soit une fonction libre déclarée « extern "C" ».

Il faudrait pouvoir donner dynamiquement l'adresse de la fonction ...
non ?


Il faut en prendre l'adresse, parce que c'est l'adresse d'une fonction
qu'attend pthread_create. Mais où en est le problème :

extern "C"
void*
threadStarter( void* p )
{
return static_cast< MaClass* >( p )->maFonction() ;
}

pthread_create( &th, NULL, &threadStarter, &maThread ) ;

Tiens, une question pour les experts : est-ce qu'une fonction templatée
puisse être « extern "C" » ? Parce que je verais bien un template pour
« threadStarter », ci-dessus.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
Jean-Marc Bourguet
writes:

Tiens, une question pour les experts : est-ce qu'une fonction templatée
puisse être « extern "C" » ? Parce que je verais bien un template pour
« threadStarter », ci-dessus.


como refuse, mais je n'ai pas fait une recherche dans la norme.

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
Falk Tannhäuser
wrote:
Tiens, une question pour les experts : est-ce qu'une fonction templatée
puisse être « extern "C" » ? Parce que je verais bien un template pour
« threadStarter », ci-dessus.

Je ne sais pas si je suis un expert, mais

____________________________________________________
extern "C" typedef void* pthreadEntryFunc(void*);

template<typename T>
struct wrap_pthreadEntry
{
static pthreadEntryFunc externCfunc;
};

template<typename T>
void* wrap_pthreadEntry<T>::externCfunc(void* p)
{
return static_cast<T*>(p)->MaFonction();
}
_____________________________________________________

est accepté par g++, ainsi que Comeau et Dinkum en ligne (je sais que cela
ne constitue pas une preuve...)

Avec g++, cela me permet de faire
_____________________________________________________
#include <iostream>
#include <ostream>
#include <pthread.h>
#include <cassert>

struct foo
{
foo* MaFonction() { std::cout << "Hello world!" << std::endl; return this; }
};

foo f;

int main()
{
pthread_t thr1;

if(pthread_create(&thr1, NULL, &wrap_pthreadEntry<foo>::externCfunc, &f) != 0)
{
std::cerr << "Error during pthtread_create" << std::endl;
return EXIT_FAILURE;
}
void* pthread_return_value = 0;
pthread_join(thr1, &pthread_return_value);
assert(pthread_return_value == &f);
return EXIT_SUCCESS;
}
_____________________________________________________

On pourrait certainement faire quelque chose de plus balèze,
prenant un pointeur sur la fonction membre en paramètre,
puis qui encapsule aussi l'appel à pthread_create, de manière
à empêcher que quelqu'un passe un pointeur sur un type incompatible
comme quatrième argument ...

Falk
Falk

1 2