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
Tobias Oed
Bertrand Mollinier Toublet wrote:

[snippage]

struct ibsm_context;

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


state est un pointeur de fonction. Il me semble que tu cherches

typdef int nouveau_state(struct ibsm_context *);


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


C'est quoi contexte ici?

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


Comme tu l'a ecrit toi meme, un typdef de fonction (ou pointeur sur
fonction) ne nomme pas les arguments de la fonction, et ne peut donc etre
utilise que comme un prototype.
int start(struct ibsm_context *);
marche mais
int start(struct ibsm_context *){qqchose}
ne marche pas. C'est pour ca que tu peux seulemnt utiliser le typdef dans
une declaration,
nouveau_state start;
devrais fonctionner.
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
Bertrand Mollinier Toublet writes:

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

parce ce que tu peux utiliser un typedef pour seulement déclarer une
fonction, pas pour la définir.

-- Gaby
Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', Bertrand Mollinier Toublet
wrote:

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:


C'est une façon de faire, pourquoi pas. Ca veut dire que c'est la transition
qui modifie l'état, ce qui ne facilite pas forcément la relecture...

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


Ok. (à part que j'aime pas l'*', un programmeur C sait ce qu'est un
pointeur).

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

enum
{
START = 0,


' = 0 ' facultatif.

DO_STUFF,
END,
STATE_COUNT
};

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

Et ma boucle principale ressemble a ca:


Quelle boucle? En multi-tâche, les automates sont souvent dans une tache. Ils
sont invoqué quand un message (évènement) arrive. La boucle active n'est pas
une fatalité.

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... */


Ah? Sans évènements extérieurs? C'est plutôt un bête séquenceur.

}

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


Ah, tu veux utiliser le type pour définir une fonction? J'y avais pensé il
y a longtemps. Ca aurait eu une chance de fonctionner avec un typedef de type
prototype et non pointeur (cad sans l'*'). Mais en fait, curieusement, le C
n'accepte pas cette syntaxe.

(Hum, pour poser cette question, un simple exemple basic aurait suffit, ce
n'était pas la peine de se lancer dans les automates. C'est mon dada, et je
risque d'être bavard sur le sujet!)

#include <stdio.h>

/* type 'fonction' */
typedef int F(void);

/* prototype de 'f' */
static F f;

int main (void)
{
{
F *pf = f;

pf();
}
return 0;
}

/* definition de 'f' */
#if 1
static int f(void)
#else
/* ne compile pas. */
static F f
#endif
{
return printf("hello worldn");
}



--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
<blank line>
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
Gabriel Dos Reis
Tobias Oed writes:

| int start(struct ibsm_context *){qqchose}
| ne marche pas.

En C++, cela marche -- il n'y aucune difficulté technique à faire
marcher ça. Mais cela n'explique pas pourquoi on ne peut pas utiliser
un typedef pour définir une fonction.

| C'est pour ca que tu peux seulemnt utiliser le typdef dans
| une declaration,

-- Gaby
Avatar
Bertrand Mollinier Toublet
Emmanuel Delahaye wrote:
In 'fr.comp.lang.c', Bertrand Mollinier Toublet
wrote:
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:


C'est une façon de faire, pourquoi pas. Ca veut dire que c'est la transition
qui modifie l'état, ce qui ne facilite pas forcément la relecture...

Qu'est ce que tu proposes comme alternative ?



Et ma boucle principale ressemble a ca:



Quelle boucle? En multi-tâche, les automates sont souvent dans une tache. Ils
sont invoqué quand un message (évènement) arrive. La boucle active n'est pas
une fatalité.

Ce n'est pas vraiment une boucle active dans la mesure ou certains etats

passent en attente (passive) d'un evenement (en l'occurence, l'arrivee
d'une ligne de caracteres sur la connection ouverte).

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... */


Ah? Sans évènements extérieurs? C'est plutôt un bête séquenceur.

Non non, les evenements exterieurs sont pris en charge, dans le contexte

d'un etat a l'interieur de la fonction correspondant a l'etat.

(merci pour la reponse a ma question originale...)

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


Avatar
Tobias Oed
Gabriel Dos Reis wrote:

| > Certains compilateurs ont même inventé la syntaxe abominable
| >
| > __attribute__((__unused__))
| >
| > pour annoter des paramètres de fonctions non utilisés. Oui, ça existe
| > et ça sert.
|
| Toi qui t'y connais, mis a part desactiver un warning est ce que gcc
| s'en sert pour autre chose?

Déficience de C qui n'autorise pas de définir une fonction qui ne
s'intéresse pas à son argument. (De plus, il n'y aucune raison
technique à cette interdiction).

Du point de vue du programmeur, c'est une autre histoire.

Imagine une API où

typedef int (*cmd_t)(Context*);

est le type d'une commande prenant un Context* et retournant un int.
(Cela par exemple peut être utiliser dans un interprèt)

cmd_t cmd[256] = {
// ....
};

où, par exemple, cmd['q'] == quit, et quit est défini comme suit

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

c'est typiquement une command qui n'a rien à foutre de son argument.
Des exemples comme cela abondent dans la nature.
Je ne te dis pas ce que cela peut être avec du code automatiquement
généré.

| J'imagine qu'une optimisation dans le code qui
| apelle une telle fonction est possible mais est ce que c'est fait?

pourquoi les gens sautent sur « optimisation » sans passer par la case
« sémantique » ?


Pour la raison suivante. En reprenant ton exemple je ne vois qu'un avantage
tres mineur entre

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

et

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

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__)).
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

--
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
Tobias Oed
Gabriel Dos Reis wrote:

Je suis impatient de la lire.


Il va falloir patienter un peu: J'ai une these a taper :(
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
Tobias Oed
Richard Delorme wrote:


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.


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. 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...)
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!

--
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
Emmanuel Delahaye
In 'fr.comp.lang.c', Bertrand Mollinier Toublet
wrote:

C'est une façon de faire, pourquoi pas. Ca veut dire que c'est la
transition qui modifie l'état, ce qui ne facilite pas forcément la
relecture...

Qu'est ce que tu proposes comme alternative ?



<HS>

Bien séparer les 4 concepts qui régissent une machine a état:

- Evènement
- Etat
- Transition
- Action

- L'évènement est une information externe ou interne entrante. On parle aussi
de stimuli ou d'entrée.

- L'état est la condition stable de la machine (même si sa durée peut être
courte)

- La transition est le changement d'état. Si la combinaison évènement/état
courant ne provoque pas de changement d'état, il n'y a pas de transition.

- L'action est la sortie associée au couple évènement/état courant. Elle
n'est pas forcément liée à une transition.

Pour caractériser une machine à état, le plus simple est de la représenter
sous la forme de listes :

- Liste des états
- Listes des évènements
- Liste des couples états/évènements avec état futur et action associée.

</>

Pour ce qui est d'une implémentation en C :

Les listes d'états et d'évènements sont bien sûr des enums basés à 0 et dont
le dernier élément est automatiquement le nombre.

typedef enum
{
STS_A,
STS_B,
STS_NB
}
eSTS;

typedef enum
{
EVT_A,
EVT_B,
EVT_C,
EVT_NB
}
eEVT;

A partir de là, on peut définir un tableau de STS_NB x EVT_NB contenat l'état
futur et l'action.

typedef int action_f (void* pdata);

typedef struct
{
eSTS next_sts;
action_f *action;
}
transition_s;

static transition_s a_transition [STS_NB][EVT_NB];

Ensuite, on initialise chaque cellule du tableau avec

static transition_s const z = {-1, 0};

for(i = ...)
for( j = ...)
a_transition[i][j] = z;

Ce qui fait que l'on a aucune transition, et aucune action.

Enfin, on installe les transitions requises par la specifications de la
machine. Par exemple:

action_f AtoBbyA;
action_f BbyA;

/* A -> B / A */
a_transition[STS_A][EVT_A].next = STS_B;
a_transition[STS_A][EVT_A].action = AtoBbyA;

/* B / A */
a_transition[STS_B][EVT_A].action = BbyA;

etc.

On a aussi besoin de définir l'état courant, et sa valeur initiale:

static eSTS G_curr_sts = STS_A;

Ensuite, le traitement d'un évènement se fait ainsi:

int event (eEVT evt, void *p_data)
{
int err = 1;

id (evt < EVT_NB)
{
transition_s *p_curr = a_transition[G_curr_sts] + evt;

/* Transition */
if (p_curr->next_sts != -1)
{
G_curr_sts = p_curr->next_sts;
}

/* Action */
if (p_curr->action != NULL)
{
err = p_curr->action (p_data);
}
}
return err;
}

L'action se produit donc dans le nouvel état.

Sur ce principe, j'ai écrit un ADT universel 'fsm' qui permet de construire
n'importe quel machine à nombre d'état finis. Je pense en avoir déjà publié
le code sur clc... Mais je peux le refaire...

--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
<blank line>
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/


Avatar
Gabriel Dos Reis
Tobias Oed writes:

| > | Dans le cas present, Bertrand veut pouvoir
| > | utiliser du parametre.
| >
| > Ce n'est pas ce qu'il a écrit dans la deuxième version sans typedef.
|
| Je viens de relire son post, et je n'ai trouve que cette definition de start
| sans typdef a la fin de son message
|
| int start(struct ibsm_context *context)
| {
| faire_quelque_chose_avec_le_context(context);
| return l_etat_suivant;
| }
|
| Tu peux me dire a quelle "deuxieme version" tu te referres?

celle que tu as citée. Pour une raison, je n'ai pas vu le paramètre
contexte. Désolé.

| > | Comme les typedef de fonction ne nomment pas les parametres, ta syntaxe
| > | permettrai de ne definir que des fonction qui ne s'interessent a aucun
| > | de leur parametres (ce qui n'est franchement pas interessant).
| >
| > Visiblement, tu n'as pas dû beaucoup programmer en C.
|
| Effectivement, je ne suis pas mainteneur d'un compilo comme certains...
| Est ce que tu veux dire par la qu'il est commun d'avoir des fonction qui ne
| s'interessent pas a leur parametre?

Oui.

| > Certains compilateurs ont même inventé la syntaxe abominable
| >
| > __attribute__((__unused__))
| >
| > pour annoter des paramètres de fonctions non utilisés. Oui, ça existe
| > et ça sert.
|
| Toi qui t'y connais, mis a part desactiver un warning est ce que gcc s'en
| sert pour autre chose?

Déficience de C qui n'autorise pas de définir une fonction qui ne
s'intéresse pas à son argument. (De plus, il n'y aucune raison
technique à cette interdiction).

Du point de vue du programmeur, c'est une autre histoire.

Imagine une API où

typedef int (*cmd_t)(Context*);

est le type d'une commande prenant un Context* et retournant un int.
(Cela par exemple peut être utiliser dans un interprèt)

cmd_t cmd[256] = {
// ....
};

où, par exemple, cmd['q'] == quit, et quit est défini comme suit

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

c'est typiquement une command qui n'a rien à foutre de son argument.
Des exemples comme cela abondent dans la nature.
Je ne te dis pas ce que cela peut être avec du code automatiquement
généré.

| J'imagine qu'une optimisation dans le code qui
| apelle une telle fonction est possible mais est ce que c'est fait?

pourquoi les gens sautent sur « optimisation » sans passer par la case
« sémantique » ? 

[...]

| > Est-ce que tu aimes quelque chose ?
|
| Les jolie filles, le pastis et la biere. Toi?

Tout.

-- Gaby
1 2 3