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

void* et fonctions membres

6 réponses
Avatar
Lucas Levrel
Bonjour,

J'en étais resté à l'idée que void* pouvait représenter n'importe quel
pointeur, y compris de fonction. Mais voilà qu'avec des fonctions membres
ça ne marche pas :

-----
class Toto {
public:
bool foo();
} ;

bool bar();

typedef bool(Toto::*member_fun_ptr)();

typedef bool(*fun_ptr)();

int main(){
member_fun_ptr test=&Toto::foo;// typedef OK
void *ptr;
fun_ptr fp=&bar;

ptr=reinterpret_cast<void*>( fp );
fp=reinterpret_cast<fun_ptr>( ptr );

ptr=reinterpret_cast<void*>( &Toto::foo );
member_fun_ptr mfp=reinterpret_cast<member_fun_ptr>( ptr );
return 0;
}
-----

g++ me dit :
-----
In function ‘int main()’:
19:42: warning: converting from ‘bool (Toto::*)()’ to ‘void*’ [-Wpmf-conversions]
20:59: error: invalid cast from type ‘void*’ to type ‘member_fun_ptr {aka bool (Toto::*)()}’
-----

Y a-t-il un moyen pour que ça fonctionne, et si oui comment ? Il me semble
que je pourrais remplacer void* par une union de tous les types de
pointeurs que j'utiliserai, mais c'est un peu lourdingue.

Accessoirement, pourquoi cette erreur ? (Et je suis un peu surpris qu'on
ait un warning dans un sens et une erreur dans l'autre.)

Merci par avance.

--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)

6 réponses

Avatar
Lucas Levrel
Le 20 août 2015, Lucas Levrel a écrit :

J'en étais resté à l'idée que void* pouvait représenter n'importe quel
pointeur, y compris de fonction. Mais voilà qu'avec des fonctions membres ça
ne marche pas :

-----
class Toto {
public:
bool foo();
} ;

bool bar();

typedef bool(Toto::*member_fun_ptr)();

typedef bool(*fun_ptr)();

int main(){
member_fun_ptr test=&Toto::foo;// typedef OK
void *ptr;
fun_ptr fp=&bar;

ptr=reinterpret_cast<void*>( fp );
fp=reinterpret_cast<fun_ptr>( ptr );

ptr=reinterpret_cast<void*>( &Toto::foo );
member_fun_ptr mfp=reinterpret_cast<member_fun_ptr>( ptr );
return 0;
}
-----

g++ me dit :
-----
In function ‘int main()’:
19:42: warning: converting from ‘bool (Toto::*)()’ to ‘void*’
[-Wpmf-conversions]
20:59: error: invalid cast from type ‘void*’ to type ‘member_fun_ptr {aka
bool (Toto::*)()}’
-----

Y a-t-il un moyen pour que ça fonctionne, et si oui comment ? Il me semble
que je pourrais remplacer void* par une union de tous les types de pointeurs
que j'utiliserai, mais c'est un peu lourdingue.



Addendum, au cas où ça aide à résoudre le problème : toutes les fonctions
sur lesquelles je prends un pointeur sont membres de la même classe Toto.

--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Avatar
Alain Ketterlin
Lucas Levrel writes:

[...]
class Toto {
public:
bool foo();
} ;

bool bar();

typedef bool(Toto::*member_fun_ptr)();

typedef bool(*fun_ptr)();

int main(){
member_fun_ptr test=&Toto::foo;// typedef OK
void *ptr;
fun_ptr fp=&bar;

ptr=reinterpret_cast<void*>( fp );
fp=reinterpret_cast<fun_ptr>( ptr );

ptr=reinterpret_cast<void*>( &Toto::foo );
member_fun_ptr mfp=reinterpret_cast<member_fun_ptr>( ptr );
return 0;
}
-----

g++ me dit :
-----
In function ‘int main()’:
19:42: warning: converting from ‘bool (Toto::*)()’ to ‘void*’
[-Wpmf-conversions]
20:59: error: invalid cast from type ‘void*’ to type â €˜member_fun_ptr
{aka bool (Toto::*)()}’
-----





Quelques éléments à :

https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr
http://stackoverflow.com/questions/1096341/function-pointers-casting-in-c

En résumé : c'est illégal, au mieux dépendant de l'impl émentation.

Y a-t-il un moyen pour que ça fonctionne, et si oui comment ? Il me
semble que je pourrais remplacer void* par une union de tous les
types de pointeurs que j'utiliserai, mais c'est un peu lourdingue.



Addendum, au cas où ça aide à résoudre le problè me : toutes les
fonctions sur lesquelles je prends un pointeur sont membres de la mê me
classe Toto.



C'est quoi "ça" (ce qui doit fonctionner) ? Que peux-tu faire avec un
pointeur de fonction/membre dont tu ne connais pas le type ?

(Et si vraiment tu as du code qui doit fonctionner pour plusieurs types,
tu auras du mal à échapper aux template.)

-- Alain.
Avatar
Lucas Levrel
Le 21 août 2015, Alain Ketterlin a écrit :

Quelques éléments à :

https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr
http://stackoverflow.com/questions/1096341/function-pointers-casting-in-c

En résumé : c'est illégal, au mieux dépendant de l'implémentation.



Merci, je vais m'y plonger.

C'est quoi "ça" (ce qui doit fonctionner) ?



La même chose qu'avec une fonction globale ou tout autre objet : pouvoir
« caster » vers et depuis void*. Ça permet de stocker sous une seule
« forme » des pointeurs de différents types. (Je ne vois pas ce qui pose
problème pour une fonction membre si ça n'en pose pas pour une fonction
globale. Je suppose que je vais l'apprendre par les liens que tu donnes.)

Que peux-tu faire avec un pointeur de fonction/membre dont tu ne connais
pas le type ?



Bien sûr je stocke aussi un index qui identifie le type de l'objet pointé.

Par exemple j'aurais :
enum { A, B, C };
vector<int> type_objets;
vector<void*> objets;
...
switch(type_objets[i]){
case A:
typeA * objetA_ptr = reinterpret_cast<typeA*>(objets[i]);
...
case B:
...
}

(Et si vraiment tu as du code qui doit fonctionner pour plusieurs types,
tu auras du mal à échapper aux template.)



Je n'ai jamais codé de templates, mais en l'occurence le code est très
différent à l'intérieur des différents « case » ci-dessus donc je n'ai pas
l'impression que ce concept s'applique.

--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Avatar
Alain Ketterlin
Lucas Levrel writes:

https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voi dptr
http://stackoverflow.com/questions/1096341/function-pointers-casting-in-c

En résumé : c'est illégal, au mieux dépendant de l'i mplémentation.





[...]
C'est quoi "ça" (ce qui doit fonctionner) ?



La même chose qu'avec une fonction globale ou tout autre objet :
pouvoir « caster » vers et depuis void*. Ça permet de stocker sous une
seule « forme » des pointeurs de différents types. (J e ne vois pas ce
qui pose problème pour une fonction membre si ça n'en pose pas pour
une fonction globale. Je suppose que je vais l'apprendre par les liens
que tu donnes.)



A priori, ce n'est pas plus légal en C qu'en C++. Par contre, si tu
utilises un type "pointeur de fonction" (p.ex void (*)(void *)), tu peux
caster entre types différents. Cf par exemple :

http://stackoverflow.com/questions/5579835/c-function-pointer-casting-to-vo id-pointer

L'idée est que les données (void *) et les fonctions (pointeurs de
fonction/membre) ne sont pas forcément stockées dans des zones de
mémoire de même nature, ne serait-ce qu'à cause de W^X. Donc les
deux types de pointeurs ne sont pas considérés comme compatibles.

Que peux-tu faire avec un pointeur de fonction/membre dont tu ne
connais pas le type ?



Bien sûr je stocke aussi un index qui identifie le type de l'objet p ointé.

Par exemple j'aurais :
enum { A, B, C };
vector<int> type_objets;
vector<void*> objets;
...
switch(type_objets[i]){
case A:
typeA * objetA_ptr = reinterpret_cast<typeA*>(objets[i]);
...
case B:
...
}



OK, mais c'est surtout l'endroit où tu appelles ta fonction qui est
important (et la provenance des paramètres qu'elles prennent).

Il me semble que ce que tu recherches est std::function, qui te permet
de conserver des "callables", que tu construis soit avec des lambdas,
soit avec std::bind, soit avec tes propres classes. Quelques infos à :

http://en.cppreference.com/w/cpp/utility/functional

-- Alain.
Avatar
Lucas Levrel
Le 21 août 2015, Alain Ketterlin a écrit :

A priori, ce n'est pas plus légal en C qu'en C++.



Même sans aller chercher le C (que je mélange car j'ai appris sur le tas,
mais je compile avec g++), pourquoi les lignes suivantes ne donnent-elles
pas d'erreur ?
ptr=reinterpret_cast<void*>( fp );
fp=reinterpret_cast<fun_ptr>( ptr );

Par contre, si tu
utilises un type "pointeur de fonction" (p.ex void (*)(void *)), tu peux
caster entre types différents. Cf par exemple :

http://stackoverflow.com/questions/5579835/c-function-pointer-casting-to-void-pointer

L'idée est que les données (void *) et les fonctions (pointeurs de
fonction/membre) ne sont pas forcément stockées dans des zones de
mémoire de même nature, ne serait-ce qu'à cause de W^X. Donc les
deux types de pointeurs ne sont pas considérés comme compatibles.



OK, merci. W^X ?

Il me semble que ce que tu recherches est std::function, qui te permet
de conserver des "callables", que tu construis soit avec des lambdas,
soit avec std::bind, soit avec tes propres classes. Quelques infos à :

http://en.cppreference.com/w/cpp/utility/functional



Merci pour tes réponses et ces... pointeurs !

--
LL
Ἕν οἶδα ὅτι οὐδὲν οἶδα (Σωκράτης)
Avatar
Alain Ketterlin
Lucas Levrel writes:

A priori, ce n'est pas plus légal en C qu'en C++.



Même sans aller chercher le C (que je mélange car j'ai appris s ur le
tas, mais je compile avec g++), pourquoi les lignes suivantes ne
donnent-elles pas d'erreur ?
ptr=reinterpret_cast<void*>( fp );
fp=reinterpret_cast<fun_ptr>( ptr );



Ouais. Il semble que l'implémentation ait le droit de donner un sens à
cela. Voir les point 7 et 8 à :

http://en.cppreference.com/w/cpp/language/reinterpret_cast

Donc ça serait plutot "implementation-defined behavior" que "undefined
behavior" (et donc, possiblement correct mais intrinsèquement
non-portable).

[...] W^X ?



Write XOR Execute. Cela garantit qu'on ne peut pas exécuter les zones de
mémoire dans lesquelles on peut écrire (par exemple en castant un
pointeur de données -- void * -- en un pointeur de fonction). Cf.

https://en.wikipedia.org/wiki/W%5EX

-- Alain.