IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

typedef void (*HANDLER)(void);


Sujet :

C

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2011
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2011
    Messages : 59
    Points : 38
    Points
    38
    Par défaut typedef void (*HANDLER)(void);
    Bonjour à toutes et à tous,
    J'ai trouvé cette expression dans le header d'un programme en C sur microcontroleur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef void (*HANDLER)(void);
    .
    Par la suite, c'est utilisé, par exemple, dans le code de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int uneFonction (HANDLER unHandle) { ...}
    J'aimerais comprendre le fonctionnement de cette expression en C car je n'ai trouvé aucune explication en français, ni aucune explication compréhensible en anglais.
    J'aimerais, si possible, trouver des exemple d'utilisation simple de ceci.
    Je vous remercie par avance !

  2. #2
    Membre expert Avatar de jopopmk
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2011
    Messages
    1 856
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 1 856
    Points : 3 570
    Points
    3 570
    Par défaut
    Salut,

    il s'agit d'un type personnalisé (typedef) décrivant un pointeur de fonction ((*HANDLER)) dont l'emprunte nous dit que la fonction pointée ne doit rien renvoyer (void) et ne prendre aucun argument ((void)). Un exemple rapide (et un peu pourri) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    typedef void (*HANDLER)(void);
     
    void hello_en(void) { printf("Hello !\n"); }
    void goodbye_en(void) { printf("Goodbye !\n"); }
     
    void hello_fr(void) { printf("Bonjour !\n"); }
    void goodbye_fr(void) { printf("Au revoir !\n"); }
     
    void bePolite(HANDLER hello, HANDLER goodbye) {
    	hello();
    	goodbye();
    }
    int main(int argc, char *argv[], char* envp[]) {
    	bePolite(hello_en, goodbye_en);
    	bePolite(hello_fr, goodbye_fr);
    	return 0;
    }
    Résultat sur la sortie standard :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Hello !
    Goodbye !
    Bonjour !
    Au revoir !
    Plus je connais de langages, plus j'aime le C.

  3. #3
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    D'ailleurs, un typedef de pointeur de fonction sans paramètre est déconseillé, car il ne permet pas de passer un contexte à la fonction sans passer par une variable globale. Même si, pour de l'embarqué, ça peut être excusable.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 631
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 631
    Points : 10 559
    Points
    10 559
    Par défaut
    Citation Envoyé par jopopmk Voir le message
    Du code ...
    Mais cela reste des pointeurs


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    typedef void (*HANDLER)(void);
     
    void default_hello(void)   { printf("nuqneH !\n"); }
    void default_goodbye(void) { printf("Qapla' !\n"); }
     
    void hello_en(void)   { printf("Hello !\n"); }
    void goodbye_en(void) { printf("Goodbye !\n"); }
     
    void hello_fr(void)   { printf("Bonjour !\n"); }
    void goodbye_fr(void) { printf("Au revoir !\n"); }
     
    void bePolite(HANDLER hello, HANDLER goodbye) {
         if (hello != NULL) {
              hello();
         } else {
              default_hello();
         }
     
         if (goodbye != NULL) {
              goodbye();
         } else {
              default_goodbye();
         }
    }
     
    int main(int /*argc*/, char */*argv*/[], char* /*envp*/[]) {
         bePolite(hello_en, goodbye_en);
         bePolite(NULL, NULL);
         bePolite(hello_fr, goodbye_fr);
     
         return 0;
    }

    Édit: D'ailleurs j'hésite toujours entre la syntaxe bePolite(hello_en, goodbye_en); et bePolite(&hello_en, &goodbye_en); (<- avec l'opérateur adresse &).

  5. #5
    Membre expert Avatar de jopopmk
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2011
    Messages
    1 856
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 1 856
    Points : 3 570
    Points
    3 570
    Par défaut
    Pour ma défense j'avais dit qu'il était un peu pourri mon code
    Pour le main c'est un snip VS.
    Plus je connais de langages, plus j'aime le C.

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 678
    Points
    13 678
    Billets dans le blog
    1
    Par défaut
    Tout n'est pas programmation défensive dans la vie ! Ces tests ne sont pas obligatoires, il ne montre qu'une certaine approche du contrat entre la fonction et ses appelants.

  7. #7
    Membre expert Avatar de jopopmk
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2011
    Messages
    1 856
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 1 856
    Points : 3 570
    Points
    3 570
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Tout n'est pas programmation défensive dans la vie ! Ces tests ne sont pas obligatoires, il ne montre qu'une certaine approche du contrat entre la fonction et ses appelants.
    Petit HS : ça fait penser à la cellule qualité au taff à qui on doit expliquer que les fonctions statiques ne doivent pas être blindées, qu'il n'y a pas réellement de tests unitaires complets possibles, qu'elles font parti d'un tout, avec un périmètre fonctionnel bien délimité. Après, tester deux pointeurs non nuls n'est pas nécessairement la mort, le check du flag zéro est pas très coûteux en soi. Fin du HS.

    Ce que j'aimerais c'est que pascal_06 comprenne le principe du callback qui est très puissant à mon sens, ça ouvre des perspectives conceptuelles assez géniales.
    Plus je connais de langages, plus j'aime le C.

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    les fonctions statiques ne doivent pas être blindées
    Je comprends le raisonnement, mais je trouve que c'est le parfait endroit pour utiliser non pas du vrai code blindant, mais des assertions. Pour voir immédiatement, dans la build Debug, que l'appelant a fait une connerie.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2011
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2011
    Messages : 59
    Points : 38
    Points
    38
    Par défaut
    Bonjour à tous,
    je vous remercie pour vos réponses très instructives !
    Je comprend un peu mieux maintenant, à moi de manipuler un peu ce concept...

    Citation Envoyé par foetus Voir le message
    Mais cela reste des pointeurs
    Édit: D'ailleurs j'hésite toujours entre la syntaxe bePolite(hello_en, goodbye_en); et bePolite(&hello_en, &goodbye_en); (<- avec l'opérateur adresse &).
    Oui, tiens, c'est vrai, pourquoi ne pas mettre l'opérateur & ?
    Si on ne le met pas, est ce que ça veut dire qu'on passe par "valeur" (????) la fonction toute entière (excusez moi si ce que j'écris des inepties !)

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Disons que pour les pointeurs de fonction, il y a une certaine "liberté" et je ne sais pas trop à quoi elle est due. La même chose s'applique aux appels, d'ailleurs (on peut aussi bien faire pf(x) que (*pf)(x)).

    Par contre, c'est un peu plus strict en C++ dans certains cas.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  11. #11
    Membre émérite
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Points : 2 601
    Points
    2 601
    Par défaut
    Bonjour,

    La norme indique :

    • Au paragraphe 6.3.2.1 section 4
      A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.
      Donc printf est un pointeur.
    • Au paragraphe 6.5.3.2 sections 1 à 4
      1. The operand of the unary & operator shall be either a function designator, the result of a
      [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is
      not declared with the register storage-class specifier.
      2 The operand of the unary * operator shall have pointer type.
      On a le droit d'utiliser les opérateurs & et * sur un désignateur de fonction.
      3. The unary & operator yields the address [...] Otherwise, the result is
      a pointer to the object or function designated by its operand.
      En gros le & sur une fonction ne change rien.
      The unary * operator denotes indirection. If the operand points to a function, the result is
      a function designator;
      Idem pour le *.


    Du coup le parenthésage est important aussi. Par exemple si on définit une macro avec le même nom qu'une fonction le fait de parenthéser fait que l'identifiant sera reconnu comme une fonction.

    On peut résumer tout ça dans ce code (vite fait …) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <stdio.h>
     
    void test(int a)
    {
      (void) a;
      puts("test function");
    }
     
    #define test(a) puts("test macro")
     
    int main(void)
    {
      printf(" printf = %p\n",  printf);
      printf("&printf = %p\n", &printf);
      printf("*printf = %p\n", *printf);
     
      test(10);
      (test)(10);
     
      return 0;
    }
    qui donne à l'exécution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $ ./func 
     printf = 0x400450
    &printf = 0x400450
    *printf = 0x400450
    test macro
    test function

Discussions similaires

  1. Réponses: 3
    Dernier message: 11/07/2014, 09h40
  2. Réponses: 12
    Dernier message: 24/05/2013, 08h53
  3. void function() et void function(void)
    Par Kel-T dans le forum C
    Réponses: 3
    Dernier message: 10/06/2012, 12h34
  4. Réponses: 4
    Dernier message: 11/08/2008, 14h49
  5. [C#2005]Étrange... Void ou void?
    Par Xzander dans le forum Windows Forms
    Réponses: 5
    Dernier message: 27/10/2006, 19h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo