OVH Cloud OVH Cloud

Pointeurs vers fonctions

54 réponses
Avatar
Pierre Maurette
Bonjour,
Mon problème est peut-être plus C que C++, mais je le fais facilement
apparaître avec new[], et de plus c'est la réponse de la norme C++ qui
m'intéresse le plus.
Si on définit une variable pointeur de fonction :
int(*pF)(double, double) = NULL;
on a la facilité d'utiliser ou non, indifféremment, les opérateurrs * et & ,
ou plus exactement on a exceptionnellement la possibilité de les omettre :
int fonct(double x, double y) {return (int)(x * y);}
...
pF = &fonct;
pF = fonct;
...
x = pF(4.0, 2.0);
x = (*pF)(4.0, 2.0);

Question : qu'en dit la norme ? (pas pu trouver)

Si je désire un tableau de fonctions dont la taile sera déterminée à
l'exécution, je dois faire :
int(**pF)(double, double) = NULL;
Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double, double)));
x = pF[4](4.0, 2.0);
Pas beau, puisque pF n'a pas le même type dans les deux lignes, mais logique
au vu de ce qui précède.
Passons au new[] :
pF = new (int(*)(double, double))[n];
La famille du Gnou accepte, et le résultat est celui attendu. Chez Borland,
c'est non., conversion de type impossible
Les Gnou interprètent pF en fonction de l'appel de malloc() ou de new[], pas
Borland. Je ne suis pas contre, je préfèrerais même que ne soient pas
autorisées les facilités initiales, donc que le typedef s'impose de
lui-même.
Question : Qu'en dit la norme ? Qui a raison ?

Bien entendu, un typedef s'impose, et tout rentre dans l'ordre.

typedef int(*Fonc_Type)(double, double);
Fonc_Type* pF1;
Fonc_Type* pF2;
int n = 12;
pF1 = (Fonc_Type*) malloc(n * sizeof(Fonc_Type));
pF2 = new Fonc_Type[n];
pF1[5] = f1;
pF2[6] = f2;

Merci pour vous lulières,
Pierre

10 réponses

1 2 3 4 5
Avatar
Loïc Joly
Pierre Maurette wrote:
Bonjour,
Mon problème est peut-être plus C que C++, mais je le fais facilement
apparaître avec new[], et de plus c'est la réponse de la norme C++ qui
m'intéresse le plus.
Si on définit une variable pointeur de fonction :
int(*pF)(double, double) = NULL;
on a la facilité d'utiliser ou non, indifféremment, les opérateurrs * et & ,
ou plus exactement on a exceptionnellement la possibilité de les omettre :
int fonct(double x, double y) {return (int)(x * y);}
...
pF = &fonct;
pF = fonct;


Marche en vertue du 4.3 :
4.3 Functiontopointer conversion
1 An lvalue of function type T can be converted to an rvalue of type
“pointer to T.” The result is a pointer to the function.50)

...
x = pF(4.0, 2.0);
x = (*pF)(4.0, 2.0);

Question : qu'en dit la norme ? (pas pu trouver)


5.2.2 Function call
1 There are two kinds of function call: ordinary function call and
member function57) (9.3) call. A function
call is a postfix expression followed by parentheses containing a
possibly empty, commaseparated
list of
expressions which constitute the arguments to the function. For an
ordinary function call, the postfix
expression shall be either an lvalue that refers to a function (in which
case the functiontopointer
standard
conversion (4.3) is suppressed on the postfix expression), or it shall
have pointer to function type.

A noter que ce raccourci n'existe pas avec les fonctions membres.


Si je désire un tableau de fonctions dont la taile sera déterminée à
l'exécution, je dois faire :
int(**pF)(double, double) = NULL;
Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double, double)));


Une question qui n'a rien à voir : Pourquoi n'initialises-tu pas à la
déclaration ?

x = pF[4](4.0, 2.0);
Pas beau, puisque pF n'a pas le même type dans les deux lignes, mais logique
au vu de ce qui précède.
Passons au new[] :
pF = new (int(*)(double, double))[n];
La famille du Gnou accepte, et le résultat est celui attendu. Chez Borland,
c'est non., conversion de type impossible
Les Gnou interprètent pF en fonction de l'appel de malloc() ou de new[], pas
Borland. Je ne suis pas contre, je préfèrerais même que ne soient pas
autorisées les facilités initiales, donc que le typedef s'impose de
lui-même.
Question : Qu'en dit la norme ? Qui a raison ?


Ca, j'en sais rien, j'ai toujours fait un typedef sans chercher à savoir...

--
Loïc

Avatar
Jean-Marc Bourguet
"Pierre Maurette" <mmaauurreettttttee.ppiieerrrree@@ffrreeee.ffrr> writes:

Bonjour,
Mon problème est peut-être plus C que C++, mais je le fais facilement
apparaître avec new[], et de plus c'est la réponse de la norme C++ qui
m'intéresse le plus.
Si on définit une variable pointeur de fonction :
int(*pF)(double, double) = NULL;
on a la facilité d'utiliser ou non, indifféremment, les opérateurrs * et & ,
ou plus exactement on a exceptionnellement la possibilité de les omettre :
int fonct(double x, double y) {return (int)(x * y);}
...
pF = &fonct;
pF = fonct;
...
x = pF(4.0, 2.0);
x = (*pF)(4.0, 2.0);

Question : qu'en dit la norme ? (pas pu trouver)


Correct (4.3 et 5.2.2/1).

Si je désire un tableau de fonctions dont la taile sera déterminée à
l'exécution, je dois faire :
int(**pF)(double, double) = NULL;


A nouveau, pourquoi pas:

typedef int (*pointeurVersFonction)(double, double);

std::vector<pointeurVersFonction> monTableau;

Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double, double)));
x = pF[4](4.0, 2.0);
Pas beau, puisque pF n'a pas le même type dans les deux lignes, mais logique
au vu de ce qui précède.
Passons au new[] :
pF = new (int(*)(double, double))[n];
La famille du Gnou accepte, et le résultat est celui attendu. Chez Borland,
c'est non., conversion de type impossible
Les Gnou interprètent pF en fonction de l'appel de malloc() ou de new[], pas
Borland. Je ne suis pas contre, je préfèrerais même que ne soient pas
autorisées les facilités initiales, donc que le typedef s'impose de
lui-même.
Question : Qu'en dit la norme ? Qui a raison ?


Borland.
new (int(*)(double, double))[n];

S'interprète comme

(new (int(*)(double, double)))[n];

ce que tu veux c'est

new (int(*[n])(double, double));

Bien entendu, un typedef s'impose, et tout rentre dans l'ordre.


Bien entendu.

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
Pierre Maurette
"Jean-Marc Bourguet" a écrit dans le message de news:

"Pierre Maurette" <mmaauurreettttttee.ppiieerrrree@@ffrreeee.ffrr> writes:
[...]

Si je désire un tableau de fonctions dont la taile sera déterminée à
l'exécution, je dois faire :
int(**pF)(double, double) = NULL;


A nouveau, pourquoi pas:

typedef int (*pointeurVersFonction)(double, double);

std::vector<pointeurVersFonction> monTableau;
Tests. Voir la réponse faite à Loïc.



Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double,
double)));


x = pF[4](4.0, 2.0);
Pas beau, puisque pF n'a pas le même type dans les deux lignes, mais
logique


au vu de ce qui précède.
Passons au new[] :
pF = new (int(*)(double, double))[n];
La famille du Gnou accepte, et le résultat est celui attendu. Chez
Borland,


c'est non., conversion de type impossible
Les Gnou interprètent pF en fonction de l'appel de malloc() ou de new[],
pas


Borland. Je ne suis pas contre, je préfèrerais même que ne soient pas
autorisées les facilités initiales, donc que le typedef s'impose de
lui-même.
Question : Qu'en dit la norme ? Qui a raison ?


Borland.
new (int(*)(double, double))[n];

S'interprète comme

(new (int(*)(double, double)))[n];

ce que tu veux c'est

new (int(*[n])(double, double));
Of course.

Je suis bête à bouffer du foin, j'ai fait le point aujourd'hui même sur les
déclarations "à la noix", dont celle-ci ....

Merci de votre réponse,

Pierre


Avatar
Pierre Maurette
"Loïc Joly" a écrit ..
[...]
...
pF = &fonct;
pF = fonct;


Marche en vertue du 4.3 :
4.3 Functiontopointer conversion
1 An lvalue of function type T can be converted to an rvalue of type
"pointer to T." The result is a pointer to the function.50)

x = pF(4.0, 2.0);
x = (*pF)(4.0, 2.0);

Question : qu'en dit la norme ? (pas pu trouver)


5.2.2 Function call
1 There are two kinds of function call: ordinary function call and
member function57) (9.3) call. A function
call is a postfix expression followed by parentheses containing a
possibly empty, commaseparated
list of
expressions which constitute the arguments to the function. For an
ordinary function call, the postfix
expression shall be either an lvalue that refers to a function (in which
case the functiontopointer
standard
conversion (4.3) is suppressed on the postfix expression), or it shall
have pointer to function type.

A noter que ce raccourci n'existe pas avec les fonctions membres.
Merci pour cette réponse rapide. Je dois être fatigué, c'était dans les

titres. Vous allez penser que j'ai préféré poster que chercher ;-)
Même si c'est dans la norme, je trouve quand même qu'ils se foutent un peu
du monde :
pF = &fonct;
pF = fonct;
x = pF(4.0, 2.0);
x = (*pF)(4.0, 2.0);
C'est tout bon.
Mais
int tab[5];
int* tub;
printf("%dt%dn", tab, &tab);// c'est tout pareil
tub = tab;
//tub = &tab; // Non
tub[2] = 1;
//(*tub)[3] = 0; // Non

Si je désire un tableau de fonctions dont la taile sera déterminée à
l'exécution, je dois faire :
int(**pF)(double, double) = NULL;
Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double,
double)));



Une question qui n'a rien à voir : Pourquoi n'initialises-tu pas à la
déclaration ?
Certes. En fait, c'est tout con, c'est pas du code, ce sont des tests. Des

dizaines tapés puis effacés. J'ai tendance alors à segmenter, pour que le
déboguage me donne une indication plus précise. Là, je sais un peu plus vite
si c'est la déclaration ou le malloc() d'initialisation qui foire.

x = pF[4](4.0, 2.0);
Pas beau, puisque pF n'a pas le même type dans les deux lignes, mais
logique


au vu de ce qui précède.
Passons au new[] :
pF = new (int(*)(double, double))[n];
La famille du Gnou accepte, et le résultat est celui attendu. Chez
Borland,


c'est non., conversion de type impossible
Les Gnou interprètent pF en fonction de l'appel de malloc() ou de new[],
pas


Borland. Je ne suis pas contre, je préfèrerais même que ne soient pas
autorisées les facilités initiales, donc que le typedef s'impose de
lui-même.
Question : Qu'en dit la norme ? Qui a raison ?


Ca, j'en sais rien, j'ai toujours fait un typedef sans chercher à
savoir...

me too. Et puis, des tableaux de fonction, c'est pas tous les jours. Des
pointeurs, oui, on en utilise : les atexit() et assimilés, les fonctio,ns
genre hook passées à l'OS; etc. Pour les autres utilisations, avec le C++
et le polymorphisme des fonctions, sans parler de celui des classes, ça perd
de son intérêt.


Merci again,
Pierre


Avatar
kanze
Loïc Joly wrote in message
news:<bt9ool$8r2$...
Pierre Maurette wrote:


[...]

Si je désire un tableau de fonctions dont la taile sera déterminée à
l'exécution, je dois faire :
int(**pF)(double, double) = NULL;



Moi, je ferais plutôt :
std::vector< int (*)( double, double ) > tableau ;

Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double, double)));



Si on veut travailler à un si bas niveau...

x = pF[4](4.0, 2.0);

Pas beau, puisque pF n'a pas le même type dans les deux lignes, mais
logique au vu de ce qui précède.



Je ne comprends pas cette phrase. Une variable a toujours le même type.
Le type de pF est toujours « int (**)( double, double ) ».

Passons au new[] :
pF = new (int(*)(double, double))[n];



Tu alloues *une* pointeur à une fonction, que tu déréférences
immédiatement au moyen de l'opérateur []. L'expression à droit se
parenthèse :
(new (int (*)( double, double )))[ n ]
L'expression est légale (syntactiquement, au moins), mais a le type int
(double, double) (c-à-d le type d'une fonction, et non un pointeur à
fonction). Il y a donc une différence de types entre la côté gauche et
la côté droite de l'affectation.

Sans doute ce que tu voulais faire, c'est :

pF = new int (*[n])( double, double ) ;

Mais je me démande bien pourquoi. Je ne vois aucun cas où j'utiliserais
new[] à la place de std::vector.

La famille du Gnou accepte, et le résultat est celui attendu. Chez
Borland, c'est non., conversion de type impossible Les Gnou
interprètent pF en fonction de l'appel de malloc() ou de new[], pas
Borland. Je ne suis pas contre, je préfèrerais même que ne soient
pas autorisées les facilités initiales, donc que le typedef s'impose
de lui-même. Question : Qu'en dit la norme ? Qui a raison ?



Selon la norme, c'est Borland qui a raison. C'est une erreur ou une
extension (selon les points de vue) connue de g++.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16


Avatar
Pierre Maurette
a écrit dans le message
[...]
Si je désire un tableau de fonctions dont la taile sera déterminée à
l'exécution, je dois faire :
int(**pF)(double, double) = NULL;



Moi, je ferais plutôt :
std::vector< int (*)( double, double ) > tableau ;
Ouf, je m'inquiétais de ne pas vous voir sur ce coup-là.

J'a répondu à Jean-Marc Bourguet que je faisias des tests.
Mais effectivement, je dois également, avant tout, tester le std::vector<>.
J'ai un lobe du cerveau qui fait de la résistance, celui qui écrit asm{bla
bla}dès qu'on le laisse seul à la maison;-)

Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double,
double)));




Si on veut travailler à un si bas niveau...
Ou en C. Pas taper, pas répondre. Connais la réponse ....


x = pF[4](4.0, 2.0);

Pas beau, puisque pF n'a pas le même type dans les deux lignes, mais
logique au vu de ce qui précède.



Je ne comprends pas cette phrase. Une variable a toujours le même type.
Le type de pF est toujours « int (**)( double, double ) ».
Pas grave, me suis mal exprimé. Je faisais référence au fait que j'étais

troublé par :
pF = &fonct;
pF = fonct; // pareil

x = pF(4.0, 2.0);
x = (*pF)(4.0, 2.0); // pareil

Je continue à trouver ça gênant, mais j'accepte. Que faire d'autre ?


Passons au new[] :
pF = new (int(*)(double, double))[n];



Tu alloues *une* pointeur à une fonction, que tu déréférences
immédiatement au moyen de l'opérateur []. L'expression à droit se
parenthèse :
(new (int (*)( double, double )))[ n ]
L'expression est légale (syntactiquement, au moins), mais a le type int
(double, double) (c-à-d le type d'une fonction, et non un pointeur à
fonction). Il y a donc une différence de types entre la côté gauche et
la côté droite de l'affectation.

Sans doute ce que tu voulais faire, c'est :

pF = new int (*[n])( double, double ) ;

Mais je me démande bien pourquoi. Je ne vois aucun cas où j'utiliserais
new[] à la place de std::vector.

La famille du Gnou accepte, et le résultat est celui attendu. Chez
Borland, c'est non., conversion de type impossible Les Gnou
interprètent pF en fonction de l'appel de malloc() ou de new[], pas
Borland. Je ne suis pas contre, je préfèrerais même que ne soient
pas autorisées les facilités initiales, donc que le typedef s'impose
de lui-même. Question : Qu'en dit la norme ? Qui a raison ?



Selon la norme, c'est Borland qui a raison. C'est une erreur ou une
extension (selon les points de vue) connue de g++.
Oui, oui. J'ai répondu au messge de Jean-Marc, et comme je n'ai pas de

passion excessive pour l'auto-flagellation.
En fait, l'histoire était partie du fait que g++ l'accepte. J'avais cherché
à interpréter à l'aune de la double notation Fonct/&Fonct et pF/(*pF).
Tout va bien à présent.
Cordialement,
Pierre



Avatar
kanze
"Pierre Maurette" <mmaauurreettttttee.ppiieerrrree@@ffrreeee.ffrr> wrote
in message news:<3ff94456$0$7160$...
a écrit dans le message
[...]
Si je désire un tableau de fonctions dont la taile sera
déterminée à l'exécution, je dois faire :
int(**pF)(double, double) = NULL;



Moi, je ferais plutôt :
std::vector< int (*)( double, double ) > tableau ;


Ouf, je m'inquiétais de ne pas vous voir sur ce coup-là. J'a répondu
à Jean-Marc Bourguet que je faisias des tests. Mais effectivement, je
dois également, avant tout, tester le std::vector<>. J'ai un lobe du
cerveau qui fait de la résistance, celui qui écrit asm{bla bla}dès
qu'on le laisse seul à la maison;-)


Ce n'est pas une raison. J'ai écrit de l'assembleur pendant au moins dix
ans avant de passer aux langages évolués. Ça se corrige:-).

Ensuite, en C comme en C++ :
int n = 12;
pF = (int(**)(double, double))malloc(n * sizeof(int(*)(double, double)));



Si on veut travailler à un si bas niveau...


Ou en C. Pas taper, pas répondre. Connais la réponse ....


Ou en C, oui.

x = pF[4](4.0, 2.0);

Pas beau, puisque pF n'a pas le même type dans les deux lignes,
mais logique au vu de ce qui précède.



Je ne comprends pas cette phrase. Une variable a toujours le même
type. Le type de pF est toujours « int (**)( double, double ) ».


Pas grave, me suis mal exprimé. Je faisais référence au fait que
j'étais troublé par :
pF = &fonct;
pF = fonct; // pareil

x = pF(4.0, 2.0);
x = (*pF)(4.0, 2.0); // pareil

Je continue à trouver ça gênant, mais j'accepte. Que faire d'autre ?


En effet, que faire d'autre. Moi non plus, ce n'est pas que je l'aime,
et dans les faits, j'écris toujours (*pF)( 4.0, 2.0 ). Mais ça me gène
moins que, par exemple :

char c ;
c = 3.14 ;

Là, les types des deux côtés de l'affectation ne sont pas les même. Mais
ça marche. Avec la différence qu'il y a une perte d'information qu'il
n'y a pas avec pF = fonct.

Dans l'ensemble, je n'aime pas les conversions implicites.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16




Avatar
Michel Michaud
Dans news:,
et dans les faits, j'écris toujours (*pF)( 4.0, 2.0 ). Mais ça me


Même dans les fonctions template acceptant indifféremment un
pointeur sur une fonction ou un objet fonction ? On ne peut
pas s'en sortir : en C++, plus qu'en C (!!!), on a besoin de
la possibilité de ne pas déréférencer explicitement les pointeurs
sur fonction. (En fait, je me demande si, un jour, on ne trouvera
pas que c'est une erreur que ce ne soit pas le cas pour les
pointeurs sur fonction membre, d'autant plus que ça semble être
une limitation tout à fait gratuite qui n'apporte rien de
particulier.)

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Gabriel Dos Reis
writes:

| Ce n'est pas une raison. J'ai écrit de l'assembleur pendant au moins dix
| ans avant de passer aux langages évolués.

Tu comptes le C comme de l'assembleur ou comme un langage évolué.

-- Gaby
Avatar
Gabriel Dos Reis
"Michel Michaud" writes:

| Dans news:,
| > et dans les faits, j'écris toujours (*pF)( 4.0, 2.0 ). Mais ça me
|
| Même dans les fonctions template acceptant indifféremment un
| pointeur sur une fonction ou un objet fonction ? On ne peut
| pas s'en sortir : en C++, plus qu'en C (!!!), on a besoin de
| la possibilité de ne pas déréférencer explicitement les pointeurs
| sur fonction.

Ce n'est pas indispensable. C'est juste une convention ; cette
convention est problablement basée sur l'observation qu'il y a pas mal
de pratique où les gens ne déréferencent pas directement les pointeurs
sur fonction. Cela réflète probablement aussi la culture/pratique des
auteurs de la STL.

| (En fait, je me demande si, un jour, on ne trouvera
| pas que c'est une erreur que ce ne soit pas le cas pour les
| pointeurs sur fonction membre, d'autant plus que ça semble être
| une limitation tout à fait gratuite qui n'apporte rien de
| particulier.)

La dernière fois que j'ai posé la question à qui de droit (cela devait
être en 1998), la réponse était qu'il préférait que les gens soient
plus explicites en ce qui concerne les histoires de type -- si tu
regardes bien, en fait tu remarqueras que la syntaxe est une extension
simple et direct de la syntaxe K+R.

Et puis, un pointeur sur une fonction-membre non-statique est une bête
vraiment différente des pointeurs sur fonctions ordinaires ou des
objets fonctionnels (sur un plan plus conceptuel). Ce qui serait
proche d'un objet fonctionnel, ce serait ce que M$ appelle
« bound-pointer », i.e. un machin qui a déjà lié la valeur de « this »
dans un « pseudo pointeur sur fonction membre ». E.g.

object.*pmf

où « object » désigne un objet et « pmf » est un pointeur sur fonction
membre non-statique.

-- Gaby
1 2 3 4 5