[Résolu] Un void dans une variable ?

Bonjour !

Je souhaite faire un système de menu avec mon écran LCD (Nokia5510 si ça intéresse quelqu'un).
J'ai besoin d'une fonction qui va stocker ce que va faire le programme quand on rentre dans un menu.
L'exemple est plus parlant:

  menu.SetSubMenu(1, myAlarm.TestRelay());
  menu.SetSubMenu(2, myAlarm.ShowDateMenu());
  menu.SetSubMenu(3, myAlarm.ShowTimeMenu());

Avec la fonction suivante:

void Menu::SetSubMenu(int id, void (*function))
{
	subMenu[id] = (*function);
}

Je ne sais pas si il est possible de stocker la fonction qui devrait être appelé plus tard.
En fait quand on valide par "OK" on va exécuter la fonction suivante:

void Menu::ValidOption()
{
	(*subMenu[displayedOption])();
}

J'aimerai que ce code exécute la fonction précédemment enregistré.

Je ne sais pas si ça pourrait marcher, mais lorsque je compile j'ai cette erreur:
error: invalid use of void expression
pour chaque menu.SetSubMenu appelé, peut importe ce qu'il y a dans le void SetSubMenu :slight_smile:

Petite image Xmind6:

Pouvez vous m'aider ?

Merci !
Damien.

Bonjour,

La déclaration des pointeur de fonction est un peu spéciale. Même moi qui pratique le C et C++ depuis longtemps je dois y réfléchir à deux ou trois fois avant de trouver la syntaxe correcte.

// déclaration variable
void (*subMenu[10])(void);

// affectation variable
void Menu::SetSubMenu(int id, void (*function)(void))
{
 subMenu[id] = function;
}

// appel fonction
 subMenu[id]();

Par contre sauf erreur de ma part je pense que la fonction doit être statique. Je ne pense pas qu'on puisse faire ça avec des fonctions non statiques d'une classe

Fonctions statiques ou non, j'ai les mêmes erreurs sur ces trois lignes:

  menu.SetSubMenu(1, myAlarm.TestRelay());
  menu.SetSubMenu(2, myAlarm.ShowDateMenu());
  menu.SetSubMenu(3, myAlarm.ShowTimeMenu());

error: invalid use of void expression

Merci tout de même de ta réponse :slight_smile:

En y allant petit à petit c'est compréhensible et gérable:

1/ Si vous voulez définir un pointeur sur une fonction, ça s'écrit comme cela

void (*pf)(); // pf est un pointeur sur une fonction qui retourne rien

2/ souvent on fait un truc comme ça pour se simplifier la vie:

typedef void (*ptrFonction)(); // ptrFonction est maintenant un identifiant synonyme de pointeur sur une fonction ne retournant rien

et donc vous pouvez l'utiliser comme un type:

ptrFonction pf; // pf est un pointeur sur une fonction qui retourne rien

3/ une fois que vous avez votre pointeur, si votre programme (ou un .h) connait des fonctions

extern void fonctionA();
extern void fonctionB();

vous pouvez faire des affectations variées avec l'adresse (&) de la fonction

ptrFonction pf;
if ([i]condition[/i]) pf = [color=blue]&[/color]fonctionA; else pf = [color=blue]&[/color]fonctionB;

ça marche avec tout ce que vous connaissez déjà donc ça fonctionne aussi comme ça:

pf = [i]condition[/i] ? &fonctionA : &fonctionB;

pf est donc maintenant un pointeur sur une fonction dépendant de la condition

4/ Pour appeler maintenant la fonction

pf est un pointeur sur la fonction, donc il faut aller chercher la fonction elle même (*pf) et on passe les paramètres (*pf)(param1, param2, param3);. Notez les parenthèses autour le *pf, elles sont super importantes pour la précédence des opérations.

cela dit les compilateurs sont malins et comme il n'y a pas d'ambiguïté sémantique, si vous écrivez
pf(param1, param2, param3);
ça va fonctionner quand même et c'est plus simple :slight_smile:

avec ça vous devriez pouvoir vous débrouiller, non?

LDami:
Fonctions statiques ou non, j'ai les mêmes erreurs sur ces trois lignes:

  menu.SetSubMenu(1, myAlarm.TestRelay());

menu.SetSubMenu(2, myAlarm.ShowDateMenu());
  menu.SetSubMenu(3, myAlarm.ShowTimeMenu());




error: invalid use of void expression

Merci tout de même de ta réponse :)

Bien sur, il faut passer l'adresse et non le résultat de la fonction

  menu.SetSubMenu(1, myAlarm.TestRelay);
...

Merci pour vos réponses !

J-M-L: ça fonctionne (en tout cas ça compile), mais j'ai tout de même une erreur quand je tente d'assigner la variable:

Le typedef (j'ai pas réussi à le mettre dans la classe, tant pis !)

typedef void (*subMenuFunctionsTypeDef)();
  subMenuFunctionsTypeDef subMenu[MAX_MENU_OPTIONS];

  subMenu[1] = &myAlarm.TestRelay;
  subMenu[2] = &myAlarm.ShowDateMenu;
  subMenu[3] = &myAlarm.ShowTimeMenu;

  menu.SetSubMenu(subMenu);
Menu::Menu()
{
	subMenuFunctionsTypeDef subMenuFunctions[MAX_MENU_OPTIONS];
}
void Menu::SetSubMenu(subMenuFunctionsTypeDef subMenu)
{
	subMenuFunctions = subMenu;
}

Erreur: error: no matching function for call to 'Menu::SetSubMenu(void (* [10])())'
Sur la ligne: menu.SetSubMenu(subMenu);

kamill: Merci pour ces détails :wink:

j'ai l'impression que vous essayez d'affecter une méthode pas une fonction générique.

  subMenu[1] = &myAlarm.TestRelay;

c'est comment votre structure de classe? ce code il est où? myAlarm, c'est une instance?

La fonction TestRelay est une fonction membre de la classe Alarm.
myAlarm est une instance de cette classe.

.h
class Alarm
{
	public:
		
             static void TestRelay();
};

.cpp
void Alarm::TestRelay()
{
	screen.SendString("En cours", 2);
	TestRelay(1, 2000);
	delay(1000);
	TestRelay(2, 2000);
}

.ino
Alarm myAlarm;

Vous pensez qu'il est impossible de stocker une méthode dans une variable void ?

Bonjour,

LDami:

  subMenuFunctionsTypeDef subMenu[MAX_MENU_OPTIONS];

...




Erreur: _error: no matching function for call to 'Menu::SetSubMenu(void (* [10])())'_
Sur la ligne: *menu.SetSubMenu(subMenu);*[/code][/code]

Tu essaies de passer une table submenu dans ta fonction au lie d'un élément (pat exemple submeneu[0].

LDami:
Bonjour !
Je souhaite faire un système de menu avec mon écran LCD (Nokia5510 si ça intéresse quelqu'un).
J'ai besoin d'une fonction qui va stocker ce que va faire le programme quand on rentre dans un menu.
...
Merci !
Damien.

Damien voici un exemple qui pourra sans doute vous inspirer

/* ********************************************* */
/*                                               */
/*   Petite classe juste pour la démo            */
/*                                               */
/* ********************************************* */

class classeDeDemo
{
  public:
    classeDeDemo(int valeurCoucou, const char * nom);
    void methodeCoucou1();
    void methodeCoucou2();

  private:
    int _maValeur;
    char _nomDeLinstance[10];
};

classeDeDemo::classeDeDemo(int valeurCoucou, const char * nom)
{
  _maValeur = valeurCoucou;
  strncpy ( _nomDeLinstance, nom, 9);
}

void classeDeDemo::methodeCoucou1()
{
  Serial.print("coucou 1 depuis l'instance = [");
  Serial.print(_nomDeLinstance);
  Serial.print("] valeur = ");
  Serial.println(_maValeur);
}

void classeDeDemo::methodeCoucou2()
{
  Serial.print("coucou 2 depuis l'instance = [");
  Serial.print(_nomDeLinstance);
  Serial.print("] valeur = ");
  Serial.println(_maValeur);
}


/* ********************************************* */
/*                                               */
/*   Classe Menu juste pour la démo              */
/*                                               */
/* ********************************************* */

class menuDeDemo
{
  public:
    menuDeDemo(int menuNb, classeDeDemo * monInstanceDeDemo, void (classeDeDemo::*ptrFonction)(void));
    void disCoucou();
    void changePointeurDeFonction(void (classeDeDemo::*ptrFonction)(void));
    void changeInstance(classeDeDemo * monInstance);

  private:
    void (classeDeDemo::*_ptrFonction)(void);
    classeDeDemo* _monInstanceDeDemo;
    int _menuNb;
};

menuDeDemo::menuDeDemo(int menuNb, classeDeDemo * monInstance, void (classeDeDemo::*ptrFonction)(void))
{
  _ptrFonction = ptrFonction;
  _monInstanceDeDemo = monInstance;
  _menuNb = menuNb;
}

void menuDeDemo::changePointeurDeFonction(void (classeDeDemo::*ptrFonction)(void))
{
  _ptrFonction = ptrFonction;
}

void menuDeDemo::changeInstance(classeDeDemo * monInstance)
{
  _monInstanceDeDemo = monInstance;
}


void menuDeDemo::disCoucou()
{
  Serial.print("Menu ");
  Serial.print(_menuNb);
  Serial.print(" ---> ");
  ((*_monInstanceDeDemo).*_ptrFonction)();
}

/* ********************************************* */
/*                                               */
/*   Le programme                                */
/*                                               */
/* ********************************************* */

classeDeDemo demo1(1, "demo1");
classeDeDemo demo2(2, "demo2");
classeDeDemo demo3(3, "demo3");

menuDeDemo monMonu1(1, &demo1, &classeDeDemo::methodeCoucou1);
menuDeDemo monMonu2(2, &demo2, &classeDeDemo::methodeCoucou2);
menuDeDemo monMonu3(3, &demo3, &classeDeDemo::methodeCoucou1);

void setup() {
  Serial.begin(115200);

  /* on teste si ça fonctionne */
  monMonu1.disCoucou();
  monMonu2.disCoucou();
  monMonu3.disCoucou();
  Serial.println("--------------------------------------------");

  /* on change le pointeur de fonction */
  monMonu1.changePointeurDeFonction(&classeDeDemo::methodeCoucou2);
  monMonu2.changePointeurDeFonction(&classeDeDemo::methodeCoucou1);
  monMonu3.changePointeurDeFonction(&classeDeDemo::methodeCoucou2);

  /* on teste de nouveau si ça fonctionne */
  monMonu1.disCoucou();
  monMonu2.disCoucou();
  monMonu3.disCoucou();
  Serial.println("--------------------------------------------");

  /* on échange les objets pointés par le menu, mais on garde les fonctions  */
  monMonu1.changeInstance(&demo3);
  monMonu2.changeInstance(&demo1);
  monMonu3.changeInstance(&demo2);

  /* on teste de nouveau si ça fonctionne */
  monMonu1.disCoucou();
  monMonu2.disCoucou();
  monMonu3.disCoucou();

}

void loop() {}

dites moi si vous avez du mal à comprendre ce que ça fait, c'est globalement assez simple

l'appel d'une méthode définie par son pointeur sur une instance dont on a le pointeur et se fait par

  ((*_monInstanceDeDemo).*_ptrFonction)();

Bonjour !

Donc j'ai fait comme tu m'a dit:

Librairie Alarm.cpp:

class Alarm
{
  public:
    Alarm();
    void TestRelay();
};
void Alarm::TestRelay() { // Instructions }

class Menu
{
  public:
    Menu();
    void SetSubMenu(Alarm * monInstance, void (Alarm::*monSubMenu[])(void));
  private:
    void (Alarm::*subMenuFunctions[MAX_MENU_OPTIONS])(void);
    Alarm* subMenuInstance;
};
void Menu::SetSubMenu(Alarm * monInstance, void (Alarm::*monSubMenu[])(void))
{
    subMenuInstance = monInstance;
    *subMenuFunctions = *monSubMenu;
}
void Menu::ValidOption()
{
    ((*subMenuInstance).*subMenuFunctions[displayedOption])();
}

Code .ino:

Alarm myAlarm;
Menu menu;
void (Alarm::*subMenu[MAX_MENU_OPTIONS])(void);

subMenu[1] = &Alarm::TestRelay;
subMenu[2] = &Alarm::ShowDateMenu;
subMenu[3] = &Alarm::ShowTimeMenu;

menu.SetSubMenu(&myAlarm, subMenu);

Évidemment j'ai retranscrit pour que ça corresponde à mes besoins.
Le fait que la variable qui contient les void soit un tableau peut poser problème?

Ton code compile bien, mais pas le mien. J'ai pleins d'erreurs du style:

undefined reference to `Menu::displayedOption'

Les codes complets sont à cette adresse:
Class Menu: Class Menu - Pastebin.com
Class Alarm: Class Alarm - Pastebin.com
J'ai gardé quelques bouts de codes, ils ne sont pas très intéressant pour vous !

L'erreur parlé précédemment se trouve par exemple, à la ligne 75 du pastebin ClassMenu.

Merci de vos réponses ! :slight_smile:

Damien L.

Il faut que vous réfléchissiez à comment vous écririez le type suivant: tableau de pointeurs sur méthodes publiques de la classe XYZ

Et ensuite vous demander si vous allez passer ce tableau par référence ou valeur pour écrire le prototype de votre fonction

Je ne sais pas si vous vous en êtes sorti, alors pour documenter une solution pour le forum, voici un bout de code qui montre comment traiter un tableau de pointeur sur les méthodes d'une autre classe

/* ********************************************* */
/*                                               */
/*   Petite classe juste pour la démo            */
/*                                               */
/* ********************************************* */
class classeDeDemo
{
  public:
    classeDeDemo(int valeurCoucou, const char * nom);
    void methodeCoucou1();
    void methodeCoucou2();

  private:
    int _maValeur;
    char _nomDeLinstance[10];
};

classeDeDemo::classeDeDemo(int valeurCoucou, const char * nom)
{
  _maValeur = valeurCoucou;
  strncpy ( _nomDeLinstance, nom, 9);
}

void classeDeDemo::methodeCoucou1()
{
  Serial.print("coucou 1 depuis l'instance = [");
  Serial.print(_nomDeLinstance);
  Serial.print("] valeur = ");
  Serial.println(_maValeur);
}

void classeDeDemo::methodeCoucou2()
{
  Serial.print("coucou 2 depuis l'instance = [");
  Serial.print(_nomDeLinstance);
  Serial.print("] valeur = ");
  Serial.println(_maValeur);
}

/* ********************************************* */
/*                                               */
/*   Classe Menu juste pour la démo              */
/*                                               */
/* ********************************************* */

// pour la lisibilité on définit le type
// foncPtr est  un pointeur sur une methode de la classe classeDeDemo ne prenant aucun param et retournant rien
typedef void (classeDeDemo::*foncPtr)(void);

class menuDeDemo
{
  public:
    menuDeDemo(int menuNb, classeDeDemo * monInstanceDeDemo);
    void disCoucou();
    void ajoutePointeurDeFonction(foncPtr arrayOfptrFonction);
    void changeInstance(classeDeDemo * monInstance);

  private:
    foncPtr _arrayOfptrFonction[4];
    classeDeDemo* _monInstanceDeDemo;
    int _menuNb;
};

menuDeDemo::menuDeDemo(int menuNb, classeDeDemo * monInstance)
{
  for (int i = 0; i < 4; i++) {
    _arrayOfptrFonction[i] = NULL;
  }
  _monInstanceDeDemo = monInstance;
  _menuNb = menuNb;
}

void menuDeDemo::ajoutePointeurDeFonction(foncPtr arrayOfptrFonction)
{
  // juste pour la démo, je n'ajoute pas si le tableau est plein
  for (int i = 0; i < 4; i++) {
    if (_arrayOfptrFonction[i] == NULL) { // place vide, on y met la fonction
      _arrayOfptrFonction[i] = arrayOfptrFonction;
      break;
    }
  }
}

void menuDeDemo::changeInstance(classeDeDemo * monInstance)
{
  _monInstanceDeDemo = monInstance;
}


void menuDeDemo::disCoucou()
{
  for (int i = 0; i < 4; i++) {
    if (_arrayOfptrFonction[i] == NULL) break; else {
      Serial.print("Menu ");
      Serial.print(_menuNb);
      Serial.print(" ---> ");
      ((*_monInstanceDeDemo).*(_arrayOfptrFonction[i]))();
    }
  }
}

/* ********************************************* */
/*                                               */
/*   Le programme                                */
/*                                               */
/* ********************************************* */

classeDeDemo demo1(1, "demo1");
classeDeDemo demo2(2, "demo2");
classeDeDemo demo3(3, "demo3");

menuDeDemo monMenu1(1, &demo1);
menuDeDemo monMenu2(2, &demo2);

void setup() {
  Serial.begin(115200);

  // mon menu1 dis 2 fois coucou1 et 1 fois coucou2 sur demo1
  monMenu1.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou1);
  monMenu1.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou1);
  monMenu1.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou2);

  // mon menu2 dis 3 fois coucou1 sur demo2
  monMenu2.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou1);
  monMenu2.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou1);
  monMenu2.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou1);

  /* on teste si ça fonctionne */
  Serial.println("\n\n--------------------------------------------");
  monMenu1.disCoucou();
  Serial.println("*****");
  monMenu2.disCoucou();
  Serial.println("--------------------------------------------");

  /* on ajoute une action  */
  // monMenu1 dira maintenant 2 fois coucou1 et 2 fois coucou2 sur demo1
  monMenu1.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou2);

  // monMenu1 dira maintenant 3 fois coucou1 et une fois coucou2 sur demo2
  monMenu2.ajoutePointeurDeFonction(&classeDeDemo::methodeCoucou2);

  /* on teste de nouveau si ça fonctionne */
  monMenu1.disCoucou();
  Serial.println("*****");
  monMenu2.disCoucou();
  Serial.println("--------------------------------------------");

  /* on échange les objets pointés par le menu, mais on garde les fonctions  */
  monMenu1.changeInstance(&demo3);
  monMenu2.changeInstance(&demo1);

  /* on teste de nouveau si ça fonctionne */
  monMenu1.disCoucou(); // dira maintenant 2 fois coucou1 et 2 fois coucou2 sur demo3
  Serial.println("*****");
  monMenu2.disCoucou(); // dira maintenant 3 fois coucou1 et une fois coucou2 sur demo1
  Serial.println("--------------------------------------------");
}

void loop() {}

j'ai aussi utilisé le [b]typedef[/b] pour vous montrer comment faire

typedef void (classeDeDemo::*[color=blue][b]foncPtr[/b][/color])(void);

l'affichage devrait vous donner ceci

--------------------------------------------
Menu 1 ---> coucou 1 depuis l'instance = [demo1] valeur = 1
Menu 1 ---> coucou 1 depuis l'instance = [demo1] valeur = 1
Menu 1 ---> coucou 2 depuis l'instance = [demo1] valeur = 1
*****
Menu 2 ---> coucou 1 depuis l'instance = [demo2] valeur = 2
Menu 2 ---> coucou 1 depuis l'instance = [demo2] valeur = 2
Menu 2 ---> coucou 1 depuis l'instance = [demo2] valeur = 2
--------------------------------------------
Menu 1 ---> coucou 1 depuis l'instance = [demo1] valeur = 1
Menu 1 ---> coucou 1 depuis l'instance = [demo1] valeur = 1
Menu 1 ---> coucou 2 depuis l'instance = [demo1] valeur = 1
Menu 1 ---> coucou 2 depuis l'instance = [demo1] valeur = 1
*****
Menu 2 ---> coucou 1 depuis l'instance = [demo2] valeur = 2
Menu 2 ---> coucou 1 depuis l'instance = [demo2] valeur = 2
Menu 2 ---> coucou 1 depuis l'instance = [demo2] valeur = 2
Menu 2 ---> coucou 2 depuis l'instance = [demo2] valeur = 2
--------------------------------------------
Menu 1 ---> coucou 1 depuis l'instance = [demo3] valeur = 3
Menu 1 ---> coucou 1 depuis l'instance = [demo3] valeur = 3
Menu 1 ---> coucou 2 depuis l'instance = [demo3] valeur = 3
Menu 1 ---> coucou 2 depuis l'instance = [demo3] valeur = 3
*****
Menu 2 ---> coucou 1 depuis l'instance = [demo1] valeur = 1
Menu 2 ---> coucou 1 depuis l'instance = [demo1] valeur = 1
Menu 2 ---> coucou 1 depuis l'instance = [demo1] valeur = 1
Menu 2 ---> coucou 2 depuis l'instance = [demo1] valeur = 1
--------------------------------------------

qui correspond bien à nos attentes

Bonjour,

Je pense avoir résolu le problème.

Malgré tout, j'ai encore beaucoup de problèmes dans le reste de mon code (qui n'ont rien à voir). Je vous redis si ça a fonctionné le plus vite possible.

-- EDIT --

Un grand merci à J-M-L !!
La solution fonctionne parfaitement !

Le code, vous l'avez, il au dessus ! Message: #12
Je n'ai rien fait de plus si ce n'est que je l'ai évidemment adapté à mon code.
karma added :slight_smile:

Damien L.

Bonne nouvelle !! :slight_smile: