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 :

Gestion de menu


Sujet :

C

  1. #1
    Nouveau candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2015
    Messages : 2
    Par défaut Gestion de menu
    Bonjour,

    je dois rendre un projet en programmation C, qui consiste a afficher un menu qui permet le traitement d'image sous format PPM, PGM et PBM. Cependant, pour écrire le menu, on nous a demandé de passer par des structures de données et pour chaque menu, il y a plusieurs données, notamment les actions qu'il effectue et ses sous-menus. Je vous laisse mon fichier menu.c et menu.h ci-dessous afin que vous puissiez comprendre un peu plus.

    Mon programme marche très bien quand j’implémente des fonctions void dans les actions d'un menu. Cependant, quand je cherche à implémenter une fonction avec un return, erreur de compilation tout-à-fait compréhensible car ma fonction d’implémentation (addActionMenu) n'attend que des fonctions void à implémenter. Je voudrai vous demander comment faire pour implémenter n'importe quel type de fonction dans mon menu svp, et merci !

    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
    33
    34
    35
    #ifndef __MENU_H__
    #define __MENU_H__
     
     
    //structures
    typedef struct{
      char* nom;
      void (*f) ();
    }action;
     
    typedef enum item_kind{
      SubMenu_kind,
      Action_kind,
    }item_kind;
     
    typedef union item_union{
      struct menu* SubMenu;
      action Action;
    }item_union;
     
    typedef struct item{
      item_kind Kind;
      item_union Union;
    }item;
     
    struct menu {
      struct menu* parent;
      char* nom;
      item items[9];
      int nbItem;
    };
    typedef struct menu menu;
     
     
    #endif
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    //MENU.C
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>
    #include "menu.h"
     
    //structure image
    typedef struct {
      unsigned char R;
      unsigned char G;
      unsigned char B;
      unsigned char A;
    }pixel_t;
    typedef struct {
      int h;
      int w;  
      pixel_t* pixel;
    }image;
     
    //exemple de fonction load_image que je veux implémenté dans le menu ouvrir
    image* load_image(){
      char fname[100];
      printf("Entrez le nom complet de l'image à ouvrir svp: ");
      scanf("%s", fname);
     
      image* im = malloc (sizeof(image));  
      FILE* fichier = NULL;
     
      fichier = fopen(fname, "r");
      if(fichier == NULL){
        fprintf(stderr, "Imposible d'ouvrir le fichier %s\n", fname);    
        exit(EXIT_FAILURE);
      }
      return im;
    }
     
    menu* createMenu(const char* text){
      menu* m = (menu*) malloc(sizeof(menu));
      m->parent = NULL;
      m->nom = malloc(strlen(text + 1) * sizeof(char));
      strcpy(m->nom, text);
      m->nbItem = 0;
      return m;
    }
    void addMenuAction(menu* m, const char* text, void (*f)()){
        m->items[m->nbItem].Kind = Action_kind;
        m->items[m->nbItem].Union.Action.nom = malloc(strlen(text + 1) * sizeof(char));
        strcpy(m->items[m->nbItem].Union.Action.nom, text);
        m->items[m->nbItem].Union.Action.f = f;
        m->nbItem++;
    }
    void addSubMenu(menu* m, menu* sm){
      if(sm->parent == NULL){   
        sm->parent = m;
        m->items[m->nbItem].Kind = SubMenu_kind;
        m->items[m->nbItem].Union.SubMenu = sm;
        m->nbItem++;
      }
    }
    void deleteMenu(menu* m) {
      if(m->parent == NULL) free(m);  
    }
    void viderBuffer(){
      int c = 0;
      while(c != '\n' && c != EOF) c = getchar();
    }
    void launchMenu(menu* m){
      int i, cn;
      bool p = false;
      char c;
      printf("%s\n\n", m->nom);
      for(i = 1; i <= m->nbItem; i++){
        switch(m->items[i-1].Kind){
          case SubMenu_kind:
          printf("%d - %s\n\n", i, m->items[i-1].Union.SubMenu->nom);
          break;
          case Action_kind:
          printf("%d - %s\n\n", i, m->items[i-1].Union.Action.nom);
          break;
        }
      }
      do {
        printf("Choice ?");
        c = getchar();
        cn = (int)c;
        if(cn == 112){
          p = true;
          cn = 1;
        }else cn -= 48;    
        viderBuffer();
      }while(cn > m->nbItem || cn < 1);
      cn--;
      if(p && m->parent != NULL) {
        system("clear");
        launchMenu(m->parent);
      }else if (cn > -1 && !p) {
        switch(m->items[cn].Kind){
          case Action_kind: 
          system("clear");
          m->items[cn].Union.Action.f();
          break;
          case SubMenu_kind:   
          system("clear");
          launchMenu(m->items[cn].Union.SubMenu); 
          break;
        }
      } else {
        system("clear");
      }
    }
     
    bool cont = true;
     
    void quit() {
      printf("Au revoir, fermeture du programme.\n");
      cont = false;
    }
     
    int main() {
    	menu* m;
    	menu* sm;
     
     
    	m = createMenu("Menu principal");
     
    	sm = createMenu("Fichier");
    	addSubMenu(m, sm);
    	addMenuAction(sm,"Ouvrir", load_image);
      //erreur de compilation car la fonction attend un void (*f)
      //alors que moi j'ai besoin d'un return, et comment recupérer le return?
     
    	while(cont) launchMenu(m);
     
    	deleteMenu(m);
     
    	return EXIT_SUCCESS;
     
    }

  2. #2
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 830
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par rachad_zaid Voir le message
    Je voudrai vous demander comment faire pour implémenter n'importe quel type de fonction dans mon menu svp, et merci !
    Bonjour

    Je suis désolé, tu peux pas. Ta fonction "addMenuAction" attend un pointeur sur une fonction "void", et ton type "action" est prévue pour stocker un pointeur sur une fonction "void". Tu peux pas lui passer autre chose.
    Ce que tu peux faire, en revanche, c'est considérer que toutes les fonctions (même les void) passées à "addMenuAction" renverront quelque chose. Dans ce cas, tu peux changer ta signature. Ainsi, quand tu voudras passer une fonction qui renvoie vraiment quelque chose, tu n'auras plus de souci. Mais bien entendu chaque fonction doit renvoyer le même type de chose (de l'int, du float, etc).

    Ou alors tu joues avec les union pour définir un type "action" pouvant stocker en parallèle plusieurs types de pointeurs de fonction différents. Exemple
    Code c : 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
    enum e_type {t_void=0, t_int, t_float};
    typedef  union {
      void (*vf) ();
      int (*if) ();
      float (*ff)();
    } t_multifct;
     
    typedef struct{
      char* nom;
      t_multifct fct;
      t_type type;
    } t_action;
     
    void addMenuAction(menu* m, const char* text, t_multifct *pt_f, e_type type){
        switch (type)
        {
            case t_void:
                m->items[m->nbItem].Union.Action.fct.vf = pt_f->vf;
                break;
            case t_int:
                m->items[m->nbItem].Union.Action.fct.if = pt_f->if;
                break;
            case t_float:
                m->items[m->nbItem].Union.Action.fct.ff = pt_f->ff;
                break;
        }
        m->items[m->nbItem].Union.type=type;   // Je pense qu'il faut conserver le type reçu...
        ...
    }

    Quelques remarques: ce topic n'a rien à faire dans la section "débuter" car visiblement tu ne débutes pas en C. Et essaye de nommer tes outils "t_xxx" (ou "s_xxx" ou "e_xxx" ou "u_xxx" suivant la nature de l'outil) ce qui te laissera la possibilité de nommer tes variables sans avoir de conflit.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    La piste de Sve@r est très bonne. L'autre piste, qui est peut être plus flexible, est de choisir comme type unique de retour un pointeur void. C'est le choix d'une bibliothèque comme pthread: la fonction en argument de la création de thread est void* (*) (void*). Tu peux envelopper dans une signature comme celle-ci n'importe quelle fonction, quelque soit son type de retour ou son nombre d'arguments. Il faut ensuite caster la valeur de retour pour pouvoir l'exploiter.

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 830
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par stendhal666 Voir le message
    L'autre piste, qui est peut être plus flexible, est de choisir comme type unique de retour un pointeur void...
    Super idée
    Ca m'avait aussi traversé l'esprit mais j'avais pas approfondi (flemme ? )

    Faut juste faire attention qu'avec cette façon de faire, on ne peut plus renvoyer les choses de façon classique.
    Exemple classique
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int carre(int x)
    {
        return x*x;
    }

    Exemple qui ne fonctionne pas
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void *carre(int x)
    {
        int resultat=x*x;
        return &resultat;   // Adresse d'une variable locale (donc supprimée à la fin de la fonction)
    }

    Exemple fonctionnel
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void *carre(int x)
    {
        int *resultat=malloc(sizeof(int));
        *resultat=x*x;
        return resultat;   // Faudra bien que l'appelant sache qu'il a récupéré l'adresse d'un int pour le cast et qu'il n'oublie pas le free() quand il n'aura plus besoin de son résultat !!!
    }
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Nouveau candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2015
    Messages : 2
    Par défaut
    Bonjour,

    je vous remercie pour votre rapide réponse, j'ai essayer la solution de @Sve@r mais sans de réel progrès, j'arrive toujours pas a comprendre le fonctionnement de votre méthode, je vous laisse ci dessus l'adaptation de votre méthode sur mon code qui me renvoie le warning suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
       fct.imaf = load_image();
    quant à @stendhal666 je voudrai bien essayer votre idée mais je trouve pas par où commencé si possible je vous demande de développer un peu plus l'idée et merci à vous encore une fois.

    menu.h
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    #ifndef __MENU_H__
    #define __MENU_H__
     
     
    //structures
    typedef struct {
      unsigned char R;
      unsigned char G;
      unsigned char B;
      unsigned char A;
    }pixel_t;
     
    typedef struct {
      int h;
      int w;  
      pixel_t* pixel;
    }image;
     
    typedef enum e_type {t_void = 0, t_image} e_type;
     
    typedef  union {
      void (*vf) ();
      image (*imaf) ();
    }t_multifct;
     
    typedef struct{
      char* nom;
      t_multifct fct;
      e_type type;
    }action;
     
    typedef enum item_kind{
      SubMenu_kind,
      Action_kind,
    }item_kind;
     
    typedef union item_union{
      struct menu* SubMenu;
      action Action;
    }item_union;
     
    typedef struct item{
      item_kind Kind;
      item_union Union;
    }item;
     
    struct menu {
      struct menu* parent;
      char* nom;
      item items[9];
      int nbItem;
    };
    typedef struct menu menu;
     
     
    #endif
    menu.c
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    //MENU.C
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>
    #include "menu.h"
     
     
     
    //exemple de fonction load_image que je veux implémenté dans le menu ouvrir
    image* load_image(){
      image* im = malloc (sizeof(image));  
     
      im->h = 22;
      im->w = 14;
     
      return im;
     
    }
     
    menu* createMenu(const char* text){
      menu* m = (menu*) malloc(sizeof(menu));
      m->parent = NULL;
      m->nom = malloc(strlen(text + 1) * sizeof(char));
      strcpy(m->nom, text);
      m->nbItem = 0;
      return m;
    }
    void addMenuAction(menu* m, const char* text, t_multifct pt_f, e_type type){
      m->items[m->nbItem].Kind = Action_kind;
      m->items[m->nbItem].Union.Action.nom = malloc(strlen(text + 1) * sizeof(char));
      strcpy(m->items[m->nbItem].Union.Action.nom, text);
      switch (type){
        case t_void:
          m->items[m->nbItem].Union.Action.fct.vf = pt_f.vf;
          break;
        case t_image:
          m->items[m->nbItem].Union.Action.fct.imaf = pt_f.imaf;
          break;
      }
      m->items[m->nbItem].Union.Action.type = type;
      m->nbItem++;
    }
     
    void deleteMenu(menu* m) {
      if(m->parent == NULL) free(m);  
    }
    void viderBuffer(){
      int c = 0;
      while(c != '\n' && c != EOF) c = getchar();
    }
    void launchMenu(menu* m){
      int i, cn;
      bool p = false;
      char c;
      printf("%s\n\n", m->nom);
      for(i = 1; i <= m->nbItem; i++){
        switch(m->items[i-1].Kind){
          case SubMenu_kind:
          printf("%d - %s\n\n", i, m->items[i-1].Union.SubMenu->nom);
          break;
          case Action_kind:
          printf("%d - %s\n\n", i, m->items[i-1].Union.Action.nom);
          break;
        }
      }
      do {
        printf("Choice ?");
        c = getchar();
        cn = (int)c;
        if(cn == 112){
          p = true;
          cn = 1;
        }else cn -= 48;    
        viderBuffer();
      }while(cn > m->nbItem || cn < 1);
      cn--;
      if(p && m->parent != NULL) {
        system("clear");
        launchMenu(m->parent);
      }else if (cn > -1 && !p) {
        switch(m->items[cn].Kind){
          case Action_kind: 
          system("clear");
          if(m->items[cn].Union.Action.type == t_void) m->items[cn].Union.Action.fct.vf();
          if(m->items[cn].Union.Action.type == t_image) m->items[cn].Union.Action.fct.imaf();
          break;
          case SubMenu_kind:   
          system("clear");
          launchMenu(m->items[cn].Union.SubMenu); 
          break;
        }
      } else {
        system("clear");
      }
    }
     
     
    int main() {
    	menu* m;
    	bool cont = true;
    	m = createMenu("Fichier");
      t_multifct fct;
     
      fct.imaf = load_image();
     
    	addMenuAction(m,"Ouvrir", fct, t_image);
     
    	while(cont) launchMenu(m);
     
    	deleteMenu(m);
     
    	return EXIT_SUCCESS;
     
    }

  6. #6
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Billets dans le blog
    21
    Par défaut
    Je réponds en deux parties: 1) l'implémentation de la fonction void* (*) (void*) et 2) l'intégration à l'architecture du programme

    1) L'idée est assez simple: toute fonction fn peut être traduite en une fonction void* (*) (void*) à condition de rassembler ses arguments dans une structure. Prenons l'exemple d'une fonction load_image avec la signature: image* load_image(const char* path, int x, int y, int mode):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    // définir la structure pour les arguments
    typedef struct load_im_args {
      const char* path;
      int x, y, mode;
    } im_args;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    // définir l'enveloppe 
    void* menu_load_image(void* args) {
      im_args* = args;
      return load_image(im_args->path, im_args->x, im_args->y, im_args->mode); // load_image a alloué la mémoire par malloc on peut renvoyer le pointeur par valeur
    }
    2) intégration à la structure générale du programme
    C'est plus là que je vois ton problème. En général, dans une interface à menu, la boucle principale ne fait qu'"écouter" la saisie de l'utilisateur; elle n'est pas utilisée pour traiter des données retournées par les fonctions appelées dans le menu. Sinon tu as à terme un switch géant dans la boucle principale pour exécuter des actions telles que "affiche image", "modifie image", "ferme image", "affiche palette", etc.
    Pour une conception plus modulaire il faut découpler au maximum l'interface d'une part / les fonctions de traitement d'autre part. La manière la plus simple d'y parvenir est de n'appeler que des fonctions qui ne retournent aucune valeur ou un type unique (par exemple une valeur indiquant le succès/l'échec si l'interface doit la signaler à l'utilisateur).

    Pour un système plus complexe, tu peux utiliser le design pattern Observateur. Voici un lien sur le site à son sujet (mais pour Java): http://rpouiller.developpez.com/tuto...e=page_4#LVI-G

Discussions similaires

  1. Gestion de menu unique
    Par LhIaScZkTer dans le forum ASP.NET
    Réponses: 1
    Dernier message: 26/03/2008, 17h44
  2. Gestion des Menu en fonction du rôle sous 10g
    Par ouatmad dans le forum Forms
    Réponses: 1
    Dernier message: 22/03/2008, 12h39
  3. Gestion de menu
    Par Tamus dans le forum Access
    Réponses: 1
    Dernier message: 25/01/2006, 10h29
  4. API de gestion de menu ?
    Par joseph_p dans le forum Développement Web en Java
    Réponses: 4
    Dernier message: 07/11/2005, 22h08
  5. Gestion de menu, JPanels dans une JFrame
    Par Doc.Fusion dans le forum Agents de placement/Fenêtres
    Réponses: 3
    Dernier message: 03/12/2004, 18h27

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