OVH Cloud OVH Cloud

Pointeur sur fonction

5 réponses
Avatar
Geoffrey
Bonjour,

pourquoi ne peut on pas definir un pointeur sur une fonction constante ??
comme si dessous ?

typedef void (A::*Func) ( void ) const // erreur de compilation

si je definit un pointeur sur une fonction non constante pas d'erreur de
compilation

typedef void (A::*Func)( void ) // compile

du coup si g une classe A

classe A
{
void uneFonctiondeAconst( void ) const;
void uneFonctiondeAnonconst( void );
};

et si j'ai une fonction prenant en parametre "Func"

void mafonction( Func pointer );

alors

mafonction( uneFonctiondeAconst ) // ne compile pas
mafonction( uneFonctiondeAnonconst ) // compile

finalement peut on definit un pointeur sur fonction prenant en compte aussi
bien une fonction const qu'un fonction non const ??


merci de votre aide.

5 réponses

Avatar
kanze
Geoffrey wrote:

pourquoi ne peut on pas definir un pointeur sur une fonction
constante ?? comme si dessous ?

typedef void (A::*Func) ( void ) const // erreur de compilation


On peut. Où est le problème ? Ça marche très bien chez moi
(g++).

si je definit un pointeur sur une fonction non constante pas
d'erreur de compilation

typedef void (A::*Func)( void ) // compile


Encore, cette ligne est tout à fait légale.

du coup si g une classe A

classe A
{
void uneFonctiondeAconst( void ) const;
void uneFonctiondeAnonconst( void );
};

et si j'ai une fonction prenant en parametre "Func"

void mafonction( Func pointer );

alors

mafonction( uneFonctiondeAconst ) // ne compile pas
mafonction( uneFonctiondeAnonconst ) // compile

finalement peut on definit un pointeur sur fonction prenant en
compte aussi bien une fonction const qu'un fonction non const
??


Je ne suis pas sûr ce que tu essaies de faire. S'il s'agit de
passer l'adresse d'une fonction non const à un pointeur de
fonction const, c'est normal que ça ne marche pas ; c'est une
violation de const. Si en revanche, tu as déclaré un pointeur à
fonction non const, que tu utilises avec un pointeur ou une
référence non const, on pourrait effectivement s'attendre à ce
qu'un pointeur à une fonction const marche. Or qu'il n'y a pas
de conversion implicite ; en fait, la seule conversion qui
marche, c'est un reinterpret_cast.

Mais le problème n'est pas propre aux fonctions membres (et
pointeurs à des fonctions membres). Considère le suivant :

void f( int* ) ;
void g( int const* ) ;

typedef void (*Pf)( int* ) ;

void
h()
{
Pf p ;
p = f ; // Bon...
p = g ; // Mauvais...
}

Si tu considères l'adresse de l'objet passé au this d'une
fonction membre comme un paramètre supplémentaire, ton problème
n'est qu'une manifestation particulière du problème ci-dessus
(qui existe déjà en C).

A priori, on pourrait bien permettre ces conversions implicites,
aussi bien en C qu'en C++. Si on ne le fait pas, j'imagine que
c'est parce qu'on n'a pas considéré les pointeurs à des
fonctions un type aussi important pour vouloir consacrer le
temps et l'effort nécessaire pour bien spécifier quelles
conversions sont légales, et quelles non. (Le bien spécifier est
loin d'être trivial.) On s'est contenté à ne pas supporter des
conversions implicites du tout, et de laisser aux programmeur de
faire une conversion explicite le cas où. Malheureusement, en
C++, cette conversion devient un reinterpret_cast, ce qui envoie
bien le mauvais message.

--
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
Geoffrey
a écrit dans le message de news:

Geoffrey wrote:

pourquoi ne peut on pas definir un pointeur sur une fonction
constante ?? comme si dessous ?

typedef void (A::*Func) ( void ) const // erreur de compilation

On peut. Où est le problème ? Ça marche très bien chez moi
(g++).


Effectivement, je n'ai plus l'erreur d'hier, il devait y avoir une faute de
syntaxe que j'ai corrigé a force de bidouille ...
( je compile avec VC6, désolé -_-'' )


si je definit un pointeur sur une fonction non constante pas
d'erreur de compilation

typedef void (A::*Func)( void ) // compile

Encore, cette ligne est tout à fait légale.


oui la c'est normal ^^

du coup si g une classe A

classe A
{
void uneFonctiondeAconst( void ) const;
void uneFonctiondeAnonconst( void );
};

et si j'ai une fonction prenant en parametre "Func"

void mafonction( Func pointer );

alors

mafonction( uneFonctiondeAconst ) // ne compile pas
mafonction( uneFonctiondeAnonconst ) // compile

finalement peut on definit un pointeur sur fonction prenant en
compte aussi bien une fonction const qu'un fonction non const
??

Je ne suis pas sûr ce que tu essaies de faire. S'il s'agit de
passer l'adresse d'une fonction non const à un pointeur de
fonction const, c'est normal que ça ne marche pas ; c'est une
violation de const. Si en revanche, tu as déclaré un pointeur à
fonction non const, que tu utilises avec un pointeur ou une
référence non const, on pourrait effectivement s'attendre à ce
qu'un pointeur à une fonction const marche. Or qu'il n'y a pas
de conversion implicite ; en fait, la seule conversion qui
marche, c'est un reinterpret_cast.


En fait j'ai declare un pointeur sur une fonction non const,
et j'essaye de lui passer un pointeur vers une fonction const.
Effectivement, je me heurtait a un probleme de type ...
le reinterpret_cast autorise la compilation on verra a l'execution si ca
n'explose pas ensuite :))

Mais le problème n'est pas propre aux fonctions membres (et
pointeurs à des fonctions membres). Considère le suivant :

void f( int* ) ;
void g( int const* ) ;

typedef void (*Pf)( int* ) ;

void
h()
{
Pf p ;
p = f ; // Bon...
p = g ; // Mauvais...
}

Si tu considères l'adresse de l'objet passé au this d'une
fonction membre comme un paramètre supplémentaire, ton problème
n'est qu'une manifestation particulière du problème ci-dessus
(qui existe déjà en C).

A priori, on pourrait bien permettre ces conversions implicites,
aussi bien en C qu'en C++. Si on ne le fait pas, j'imagine que
c'est parce qu'on n'a pas considéré les pointeurs à des
fonctions un type aussi important pour vouloir consacrer le
temps et l'effort nécessaire pour bien spécifier quelles
conversions sont légales, et quelles non. (Le bien spécifier est
loin d'être trivial.) On s'est contenté à ne pas supporter des
conversions implicites du tout, et de laisser aux programmeur de
faire une conversion explicite le cas où. Malheureusement, en
C++, cette conversion devient un reinterpret_cast, ce qui envoie
bien le mauvais message.

--
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



Maintenant si j'etend un peu mon probleme :

Si j'ai :

classe A
{
public:
typedef bool (*FunctPtr) ( void );
fonctA ( FunctPtr _pointer );
}

classe B : public A
{
bool fonctB(void);
}

si quelque part dans B je veux ecrire fonctA( &B::FonctB );
J'obtient normalement une erreur de compilation car on ne pas convertir de
bool (B::*) () vers bool ( * )()

La solution que j'ai mis en place pour ca est de transformer A en classe
template

template< class X>
classe A
{
public:
typedef bool (X::*FunctPtr) ( void );
fonctA ( FunctPtr _pointer );
}

classe B : public A<B>
{
bool fonctB(void);
}

maintenant je peux ecrire fonctA( &B::FonctB );

mais franchement je ne suis pas sur de la solution ...
Il existe surement d'autre moyen pour faire ce genre de chose ??

desolé pour l'exemple qui n'est pas tres parlant mais resume tres bien le
probleme que je rencontre ...

Avatar
kanze
Geoffrey wrote:
a écrit dans le message de news:

Geoffrey wrote:

Je ne suis pas sûr ce que tu essaies de faire. S'il s'agit
de passer l'adresse d'une fonction non const à un pointeur
de fonction const, c'est normal que ça ne marche pas ; c'est
une violation de const. Si en revanche, tu as déclaré un
pointeur à fonction non const, que tu utilises avec un
pointeur ou une référence non const, on pourrait
effectivement s'attendre à ce qu'un pointeur à une fonction
const marche. Or qu'il n'y a pas de conversion implicite ;
en fait, la seule conversion qui marche, c'est un
reinterpret_cast.


En fait j'ai declare un pointeur sur une fonction non const,
et j'essaye de lui passer un pointeur vers une fonction const.
Effectivement, je me heurtait a un probleme de type ... le
reinterpret_cast autorise la compilation on verra a
l'execution si ca n'explose pas ensuite :))


Tant que c'est la seule chose que tu changes avec le
reinterpret_cast, ça doit marcher. C'est un peu dommage, quand
même, qu'il n'y a pas de cast qui conviendrait mieux, qui
empêcherait que tu changes par erreur quelque chose qui ferait
que ça ne marche pas.

[...]
Maintenant si j'etend un peu mon probleme :

Si j'ai :

classe A
{
public:
typedef bool (*FunctPtr) ( void );


Donc, un pointeur à une fonction libre (ou statique).

fonctA ( FunctPtr _pointer );
}

classe B : public A
{
bool fonctB(void);
}

si quelque part dans B je veux ecrire fonctA( &B::FonctB );
J'obtient normalement une erreur de compilation car on ne pas
convertir de bool (B::*) () vers bool ( * )()


Tout à fait.

La solution que j'ai mis en place pour ca est de transformer A
en classe template

template< class X>
classe A
{
public:
typedef bool (X::*FunctPtr) ( void );
fonctA ( FunctPtr _pointer );
}

classe B : public A<B>
{
bool fonctB(void);
}

maintenant je peux ecrire fonctA( &B::FonctB );


Et qu'est-ce que functA fait avec ce pointeur ? Il faut bien
qu'il ait un pointeur à un objet de type B pour l'utiliser ; son
this ne suffit pas.

mais franchement je ne suis pas sur de la solution ...


Prèsque toujours quand j'ai commencé avec des pointeurs à des
fonctions membres, j'ai fini par les convertir en objets
fonctionnels avec un operator()() virtuel. Donc, dans ton cas :

class A
{
public:
struct AbstractCB
{
virtual ~AbstractCB() {}
virtual bool operator()() const = 0 ;
} ;
void fonctA( AbstractCB const& f ) ;
// ...
} ;

template< typename D >
class CB : public AbstractCB
{
public :
CB( D* obj, bool (D::*pfm)() )
: myObj( obj )
, myPfm( pfm )
{
}
virtual bool operator()() const
{
return (myObj->*myPfm)() ;
}
private:
D* myObj ;
bool (D::* myPfm)() ;
} ;

(Sauf que souvent, le pointeur à la fonction membre peut aussi
être paramètre du template.)

Ensuite, tu crées le CB<D> qu'il te faut, comme variable locale,
voire même comme temporaire.

--
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
Geoffrey
La solution que j'ai mis en place pour ca est de transformer A
en classe template

template< class X>
classe A
{
public:
typedef bool (X::*FunctPtr) ( void );
fonctA ( FunctPtr _pointer );
}

classe B : public A<B>
{
bool fonctB(void);
}

maintenant je peux ecrire fonctA( &B::FonctB );


Et qu'est-ce que functA fait avec ce pointeur ? Il faut bien
qu'il ait un pointeur à un objet de type B pour l'utiliser ; son
this ne suffit pas.


En fait je vais serializer des object C++ en XML ( et inversement ) et
plutot que de faire une solution bete et jetable,
je me demandais si je pouvais pas generaliser la serialisation C++ <-> XML.
J'ai donc une classe de base qui a pour membre des tableau de pointeur sur
fonction definissant pour chaque balise une fonction set et une fonction get
de la valeur a stocker.
Ainsi dans la classe derivé, je n'ai plus qu'a renseigner ce tableau pour
que le stockage et destockage de la valeur soit automitique
( y'a surement mieux comme solution )

mais franchement je ne suis pas sur de la solution ...


Prèsque toujours quand j'ai commencé avec des pointeurs à des
fonctions membres, j'ai fini par les convertir en objets
fonctionnels avec un operator()() virtuel. Donc, dans ton cas :


class A
{
public:
struct AbstractCB
{
virtual ~AbstractCB() {}
virtual bool operator()() const = 0 ;
} ;
void fonctA( AbstractCB const& f ) ;
// ...
} ;


template< typename D >
class CB : public AbstractCB
{
public :
CB( D* obj, bool (D::*pfm)() )
: myObj( obj )
, myPfm( pfm )
{
}
virtual bool operator()() const
{
return (myObj->*myPfm)() ;
}
private:
D* myObj ;
bool (D::* myPfm)() ;
} ;

(Sauf que souvent, le pointeur à la fonction membre peut aussi
être paramètre du template.)

Ensuite, tu crées le CB<D> qu'il te faut, comme variable locale,
voire même comme temporaire.


Ce que tu propose ici, ne m'aurait surement pas traverser l'esprit, je vais
m'y interesser de plus pres !
Merci pour cette orientation.


--
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
kanze
Geoffrey wrote:
La solution que j'ai mis en place pour ca est de transformer A
en classe template

template< class X>
classe A
{
public:
typedef bool (X::*FunctPtr) ( void );
fonctA ( FunctPtr _pointer );
}

classe B : public A<B>
{
bool fonctB(void);
}

maintenant je peux ecrire fonctA( &B::FonctB );


Et qu'est-ce que functA fait avec ce pointeur ? Il faut bien
qu'il ait un pointeur à un objet de type B pour l'utiliser ;
son this ne suffit pas.


En fait je vais serializer des object C++ en XML ( et
inversement ) et plutot que de faire une solution bete et
jetable, je me demandais si je pouvais pas generaliser la
serialisation C++ <-> XML. J'ai donc une classe de base qui a
pour membre des tableau de pointeur sur fonction definissant
pour chaque balise une fonction set et une fonction get de la
valeur a stocker.


En somme, quelque chose comme :

typedef void (Base::*
SetterFnc)( std::string const& value ) ;
typedef std::string (Base::*
GetterFnc)() const ;
struct Functions
{
SetterFnc pSet ;
GetterFnc pGet ;
} ;
std::map< std::string, Functions > const*
setterGetterMap ;

(Je suppose que ceci fait partie d'une base virtuelle, de façon
à ce qu'il soit toujours la classe la plus dérivée qui
l'initialise, et que le seul constructeur prend la valeur
initiale de setterGetterMap. Et que dans les classes dérivées,
évidemment, les objets dont on passent le pointeur sont des
membres statiques.)

Je ne vois pas où tu avais le problème avec le const -- tous les
getter doivent être const, et tous les setter non. Il y a un
problème potentiel, en revanche, en ce qui concerne les
conversions entre Base::* et Derived::*.

D'abord, évidemment, la converion std::string (Derived::*)()
const vers std::string (Base*::)() const n'est pas implicit. je
comprends à partir de la norme. À condition que la base ne soit
pas virtuelle, alors que c'est ce qu'on préfèrera ici. Je ne
suis pas sûr qu'il soit 100% exacte, en revanche, et je me
méfierais des compilateurs même si mon interprêtation est
correcte. G++, en tout cas, rejette le static_cast. (Les mots
exactes dans la norme sont : « If class B contains the original
member, or B is a base or derived class of the class containing
the original member, [...] ». Qui semblerait dire que ça doit
marcher, parce que dans ce cas-ci, Base (B) est un base de
Derived (qui contient le membre initial). Mais je n'ai pas assez
de confiance en mon interprêtation pour envoyer un rapport
d'erreur aux développeurs de g++. Et le faire marcher dans le
cas des héritages multiples ne me semble pas évident.)

Ainsi dans la classe derivé, je n'ai plus qu'a renseigner ce
tableau pour que le stockage et destockage de la valeur soit
automitique ( y'a surement mieux comme solution )


Selon la définition de mieux. Si « mieux » signifie, marche avec
des compilateurs courants, dont g++, oui:-).

mais franchement je ne suis pas sur de la solution ...


Prèsque toujours quand j'ai commencé avec des pointeurs à des
fonctions membres, j'ai fini par les convertir en objets
fonctionnels avec un operator()() virtuel. Donc, dans ton
cas :



[...]

Utiliserait une variante de la solution que j'ai coupée :

template< typename D, Base::Setter set, Base::Getter get >
struct CallBack : public Base::AbstractCallback
{
virtual void set( Base* pBase,
std::string const& value ) const
{
D* pD = dynamic_cast< D* >( pBase ) ;
assert( pD != NULL ) ;
(pD->*set)( value ) ;
}
virtual std::string get( Base const* pBase ) const
{
D const* pD = dynamic_cast< D const* >( pBase )
;
assert( pD != NULL ) ;
return (pD->*get)() ;
}
} ;

avec une Base::AbstractCallback qui va bien.

Note bien que dans ce cas-ci, ton map est obligatoirement vers
des Base::CallBack const*, et non des valeurs. En fait,
j'utiliserais des macros, voir de la génération externe du code,
pour générer les tables, et surtout leur initialisation. Parce
qu'une simple table, comme ci-dessus, ne ferait plus l'affaire,
étant donné que le table doit contenir des pointeurs, et l'objet
réel cible de chaque pointeur a un type différent.

D'ailleurs, il y a de fortes chances que même pour la class
CallBack, j'utiliserais un macro, ne serait-ce que pour pouvoir
écrire, dans le code, "pD->set( value )", à la place de
"(pD->*set)( value )". Ou, encore une fois, de la génération
externe du code. (La combinaison des macros et de la génération
externe de code avec des outils du type AWK et des fichiers de
définition peut être une solution extrèmement puissante pour ce
genre de problème.)

--
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