OVH Cloud OVH Cloud

Definition de fonction et typedef

22 réponses
Avatar
Bertrand Mollinier Toublet
Salut,

en me torturant les meninges sur un automates d'etats, il m'est venu
l'idee tordue suivante: definissant un contexte pour mon automate

struct ibsm_context;

et considerant mes etats comme des fonctions retournant le numero de
l'etat suivant, je definis le type:

typedef int (*state)(struct ibsm_context *);

Ainsi, ayant un tableau associant un etat avec un context:

enum
{
START = 0,
DO_STUFF,
END,
STATE_COUNT
};

state states[] =
{
start,
do_stuff,
end
};

Et ma boucle principale ressemble a ca:

void run_automate(void)
{
struct ibsm_context *context = create_context();
int current_state = START;

while (END != (current_state = states[current_state](context)))
/* RIEN ICI */ ;

/* tadam, mon automate a tourne comme il faut... */
}

Maintenant, l'idee tordue: pourquoi etant donne le typdef ci-dessus, je
ne peux pas definir mes fonctions etat de la facon suivante:

state start
{
faire_quelque_chose_avec_le_context(context);
return l_etat_suivant;
}

en lieu et place de

int start(struct ibsm_context *context)
{
faire_quelque_chose_avec_le_context(context);
return l_etat_suivant;
}

--
Bertrand Mollinier Toublet
"Reality exists" - Richard Heathfield, 1 July 2003

10 réponses

1 2 3
Avatar
Richard Delorme

Pour conserver une syntaxe plus consistante, on pourrait overloader void
et introduire au lieu de la premiere version la syntaxe suivante:

int quit(Context *void)
{
exit(0);
}

Qu'en pense tu? M'en vais de ce pas ecrire une proposition au commitee 8>
Tobias


Moi je préfère
int quit(Context *)
{
exit(0);
}

Il faut rappeler que le type void est déjà une abbération en soit, et qu'il
eût été préférable d'écrire :

f()
{
/*...*/
}

au lieu de :

void f(void)
{
/*...*/
}

Expliciter rien est quand même une drôle d'idée.

--
Richard

Avatar
Gabriel Dos Reis
Richard Delorme writes:

| Il faut rappeler que le type void est déjà une abbération en soit, et qu'il
| eût été préférable d'écrire :
|
| f()
| {
| /*...*/
| }
|
| au lieu de :
|
| void f(void)
| {
| /*...*/
| }

peut-être faut-il rappeler que Ritchie a qualifié la seconde syntaxe
d'abomination, mais le comité C a préféré l'abomination.


J'aurai bien vu un

f: {
/* ... */
}

-- Gaby
Avatar
Gabriel Dos Reis
Tobias Oed writes:

| Au niveau du type de retour, je suis bien d'accord avec toi. Il me semble
| avoir lu dans le k&r [*] qu'une fonction qui ne retourne rien utilise void
| pour que la syntaxe du language reste homogene.

Pour la petite histoire, le C inventé par K&R ne contient pas « void ».

void est une invention de quelques personnes dont Bjarne Stroustrup,
qui en avait fourni la première implémentation. void a été inventé,
entre autre, pour donner à la mémoire brute un type « void* ». Puis il
s'en est suivi des utilisations « détournées » de pour indiquer
l'absence de paramètre ou de type de retour.

| C'est en suivant cette
| logique que j'arrive a void pour le parametre non utilise. Non pas que je
| sois reelement pour (static aurait ete encore mieux.:^)
| Par contre, au niveau de f(void) et f() il y a une difference. Dans le
| premier cas c'est une fonction qui ne prends pas de parametres alors que
| dans le second cas le nombre de parametres est non specifie. (Je sais que
| t'est deja au courant...)

Ce que me paraît assez singulier, c'est qu'il faille explicitement
marquer un endroit avec un mot clé pour dire qu'il n'y a rien là...

| Tobias.
| [*] je ne retrouve pas le paragraphe; ca a du etre ailleurs surtout si selon
| Gabriel, Ritchie trouve ca abominable. Sans doute le Stroustroup, cet homme
| la est pret a tout!

Voici une partie de l'histoire :

C with Classes introduced the notation f(void) for a function f that
takes no arguments as a contrast to f() that in C declares a
function that can take any number of arguments of any type without
type check. My users soon convinced me, however, that the f(void)
notation wasn't elegant, and that having functions declared f()
accept arguments wasn't intuitive. Consequently, the result of the
experiment was to have f() mean a function f that takes no
arguments, as any novice would expect. It took support from Doug
McIlroy and Dennis Ritchie for me to build up the courage to make
this break from C. Only after they used the word /abomination/
about f(void) did I dare give f() the obvious meaning.

-- B. Stroustrup in D&E
-- Gaby
Avatar
Antoine Leca
Dans <URL:news:bfk39j$fo7fl$,
Tobias Oed écrivit:
C'est du l'ordre du syntactic sugar. Par contre si le compilo peut se
servir de cette information pour faire des optimisation alors je suis
meme pour le tres moche __attribute__((__unused__)).


J'ai du mal à comprendre laquelle : le fait de savoir que l'argument
n'est pas utilisé *peut* te faire économiser au niveau du cadre d'appel
(j'ai loupé autre chose ?) Si le compilo décide d'optimiser à ce niveau
(autrement dit, si c'est un compilo qui optimise _et_ qu'on ne lui a
pas interdit spécifiquement cela au motif par exemple que l'on veut
déboguer), il n'a pas besoin de l'indication pour se rendre compte que
l'argument n'est pas déréférencé, et donc faire l'économie.

La vraie optimisation, ce serait de ne pas passer le _paramètre_ au
moment de l'appel... Mais c'est bien autre chose, et on se heurte aux
problème de localité, compilation partagée, compatibilité K&R1, etc.

Pour conserver une syntaxe plus consistante, on pourrait overloader
void et introduire au lieu de la premiere version la syntaxe suivante:

int quit(Context *void)
{
exit(0);
}


Marche pas. Un nom d'argument (abstrait) dans un prototype peut
parfaitement être entouré de parenthèses; si tu entoures de parenthèses
ton "void", cela peut devenir un paramètre (non-nommé) de type fonction
sans paramètre.
On a déjà rencontré ce problème avec les typedefs en son temps; c'est
pour cela qu'il y a une mention explicite d'une règle pour les ambiguïtés.

Si tu mets une exception spécifique à ce niveau, tu vas te faire incendier
par ceux qui écrivent les compilateurs (et donc, ta proposition ne vaut
pas tripette au niveau du comité).


Antoine

Avatar
Tobias Oed
Gabriel Dos Reis wrote:

Tobias Oed writes:

| J'avais pense a ca:
|
| /* f.h */
|
| int f(int *);

ceci est une déclaration et non une définition. En particulier, elle
ne dit pas si la fonction se sert de son paramètre ou non.


Absolument. Manque de cafe ici. Je voulais bien une declaration mais d'une
fonction f qui ignore son argument de type int. Avec ma syntaxe pecrave:

int f(int void); /* Ahhh, la belle syntaxe lympide! */

ou pour gcc (pas sur de la syntaxe mais ca compile)

int f(int __attribute((__unused__)));

J'avais loupe le fait que la syntaxe qui consiste a ommettre le nom du
parametre ne marche que pour definir une fonction mais pas pour la
declarer.

|
| /* a.c */
|
| #include "f.h"
|
| int g(int a,int b,int c){
| return f(a*b+c);
| }
|
| Comme le compilo sait que f ne se sert pas de son argument il peut
| passer 0

Non, le compilateur ne sait rien. La déclaration de « f » ne dit pas
que la fonction « f » ne se sert pas de son paramnètre.


Avec ma modification il me semble que ca marche.


[...]

| >> Pour conserver une syntaxe plus consistante, on pourrait overloader
| >> void et introduire au lieu de la premiere version la syntaxe
| >> suivante:
| >>
| >> int quit(Context *void)
| >> {
| >> exit(0);
| >> }
| >
| > Marche pas. Un nom d'argument (abstrait) dans un prototype peut
| > parfaitement être entouré de parenthèses; si tu entoures de
| > parenthèses ton "void", cela peut devenir un paramètre (non-nommé) de
| > type fonction sans paramètre.
|
| Comprends pas. Style int quit(Context *(void)); ?

Oui.


Apres 5 minutes de grattage de tete j'ai compris. C'est la meme chose que
int quit(Context *(*)(void));

Par contre mon cdecl a un bug!
cdecl> explain int g(int *(void))
parse error
cdecl> explain int g(int *(*)(void))
declare g as function (pointer to function (void) returning pointer to int)
returning int

Tobias.

--
unix http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.programmer.html
clc http://www.eskimo.com/~scs/C-faq/top.html
fclc (french): http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
Gabriel Dos Reis
Tobias Oed writes:

| J'avais pense a ca:
|
| /* f.h */
|
| int f(int *);

ceci est une déclaration et non une définition. En particulier, elle
ne dit pas si la fonction se sert de son paramètre ou non.

|
| /* a.c */
|
| #include "f.h"
|
| int g(int a,int b,int c){
| return f(a*b+c);
| }
|
| Comme le compilo sait que f ne se sert pas de son argument il peut passer 0

Non, le compilateur ne sait rien. La déclaration de « f » ne dit pas
que la fonction « f » ne se sert pas de son paramnètre.

[...]

| >> Pour conserver une syntaxe plus consistante, on pourrait overloader
| >> void et introduire au lieu de la premiere version la syntaxe suivante:
| >>
| >> int quit(Context *void)
| >> {
| >> exit(0);
| >> }
| >
| > Marche pas. Un nom d'argument (abstrait) dans un prototype peut
| > parfaitement être entouré de parenthèses; si tu entoures de parenthèses
| > ton "void", cela peut devenir un paramètre (non-nommé) de type fonction
| > sans paramètre.
|
| Comprends pas. Style int quit(Context *(void)); ?

Oui.

-- Gaby
Avatar
Gabriel Dos Reis
Tobias Oed writes:

| Gabriel Dos Reis wrote:
|
| > Tobias Oed writes:
| >
| > | J'avais pense a ca:
| > |
| > | /* f.h */
| > |
| > | int f(int *);
| >
| > ceci est une déclaration et non une définition. En particulier, elle
| > ne dit pas si la fonction se sert de son paramètre ou non.
|
| Absolument. Manque de cafe ici. Je voulais bien une declaration mais d'une
| fonction f qui ignore son argument de type int. Avec ma syntaxe pecrave:
|
| int f(int void); /* Ahhh, la belle syntaxe lympide! */

C'est le genre de truc qui déclenche facilement « over my dead body »,
au sein du comité. Et comme dirait Antoire, cela vaudrait pas
triplette :-)

| ou pour gcc (pas sur de la syntaxe mais ca compile)
|
| int f(int __attribute((__unused__)));

Ça compile, mais cela ne fait pas ce que tu voudrais. L'__attribute__
s'applique au « declarator » (ici manquant) de paramètre, et
aucunément à la fonction « f ».

| J'avais loupe le fait que la syntaxe qui consiste a ommettre le nom du
| parametre ne marche que pour definir une fonction mais pas pour la
| declarer.
|
| > |
| > | /* a.c */
| > |
| > | #include "f.h"
| > |
| > | int g(int a,int b,int c){
| > | return f(a*b+c);
| > | }
| > |
| > | Comme le compilo sait que f ne se sert pas de son argument il peut
| > | passer 0
| >
| > Non, le compilateur ne sait rien. La déclaration de « f » ne dit pas
| > que la fonction « f » ne se sert pas de son paramnètre.
|
| Avec ma modification il me semble que ca marche.

qu'est-ce qui semble marcher exactement ?

-- Gaby
Avatar
Gabriel Dos Reis
Serge Paccalin writes:

| Le mardi 22 juillet 2003 à 22:04, Gabriel Dos Reis a écrit dans
| fr.comp.lang.c :
|
| >| au lieu de :
| >|
| >| void f(void)
| >| {
| >| /*...*/
| >| }
|
| [coupe]
|
| > J'aurai bien vu un
|
| > f: {
| > /* ... */
| > }
|
| Oui, mais pourquoi le compilateur générerait-il du code de sortie, et
| où ? À moins de rendre obligatoire l'usage de 'return;'.

Pourquoi rendre obligatoire l'usage de return ? La règle génërale
actuelle est que si le flot de controle arrive à la fin du corps
d'une fonction sans instruction return alors le programme fait comme
s'il y en avait une.

Ce que je suggérais ci-haut ressemble à une définition de label. Ce
n'est pas innocent : une définition de fonction est effectivement une
déclaration toplevel de label (c'est juste qu'on a décidé d'appeler
un label au toplevel une fonction :-)

-- Gaby
Avatar
Tobias Oed
Gabriel Dos Reis wrote:

Tobias Oed writes:

| Gabriel Dos Reis wrote:
|
| > Tobias Oed writes:
| >
| > | J'avais pense a ca:
| > |
| > | /* f.h */
| > |
| > | int f(int *);
| >
| > ceci est une déclaration et non une définition. En particulier, elle
| > ne dit pas si la fonction se sert de son paramètre ou non.
|
| Absolument. Manque de cafe ici. Je voulais bien une declaration mais
| d'une fonction f qui ignore son argument de type int. Avec ma syntaxe
| pecrave:
|
| int f(int void); /* Ahhh, la belle syntaxe lympide! */

C'est le genre de truc qui déclenche facilement « over my dead body »,
au sein du comité. Et comme dirait Antoire, cela vaudrait pas
triplette :-)

| ou pour gcc (pas sur de la syntaxe mais ca compile)
|
| int f(int __attribute((__unused__)));

Ça compile, mais cela ne fait pas ce que tu voudrais. L'__attribute__
s'applique au « declarator » (ici manquant) de paramètre, et
aucunément à la fonction « f ».


A ben m.... alors. C'est le parametre qui n'est pas utilise et pas f qui
n'utilise pas le parametre. J'ai du mal a voir la difference mais c'est
peut etre pour ca qui embrouille tout et qu'on ne se compreds pas... (suite
plus bas)

| J'avais loupe le fait que la syntaxe qui consiste a ommettre le nom du
| parametre ne marche que pour definir une fonction mais pas pour la
| declarer.
|
| > |
| > | /* a.c */
| > |
| > | #include "f.h"
| > |
| > | int g(int a,int b,int c){
| > | return f(a*b+c);
| > | }
| > |
| > | Comme le compilo sait que f ne se sert pas de son argument il peut
| > | passer 0
| >
| > Non, le compilateur ne sait rien. La déclaration de « f » ne dit pas
| > que la fonction « f » ne se sert pas de son paramnètre.
|
| Avec ma modification il me semble que ca marche.

qu'est-ce qui semble marcher exactement ?


... si la semantique de la declaration plus haut est "f n'utilise pas ce
parametre" alors a mon avis le compilo peut choisir de ne pas evaluer a*b+c
dans la fonction g (sans avoir la definition de f, juste la declaration)
car il sait que f se fiche de sa valeur.
Tobias.

--
unix http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.programmer.html
clc http://www.eskimo.com/~scs/C-faq/top.html
fclc (french): http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
Gabriel Dos Reis
Tobias Oed writes:

[...]

| > | ou pour gcc (pas sur de la syntaxe mais ca compile)
| > |
| > | int f(int __attribute((__unused__)));
| >
| > Ça compile, mais cela ne fait pas ce que tu voudrais. L'__attribute__
| > s'applique au « declarator » (ici manquant) de paramètre, et
| > aucunément à la fonction « f ».
|
| A ben m.... alors. C'est le parametre qui n'est pas utilise et pas f qui
| n'utilise pas le parametre. J'ai du mal a voir la difference mais c'est
| peut etre pour ca qui embrouille tout et qu'on ne se compreds pas... (suite
| plus bas)

dans le premier cas, c'est un attribut du paramètre, dans le deuxième
cas c'est un attribut de la fonction (il n'y a pas encore de syntaxe
pour exprimer cela).

-- Gaby
1 2 3