[Conseil] Appel d'une fonction à partir d'un tableau.

Bonjour à toutes et à tous,

Je viens vers vous car je n'arrive pas à trouver la solution à un problème.

Je désire savoir comment appeler une fonction dont le nom est contenu dans un tableau ?
Exemple :

Un premier tableau contient des données utilisées pour l'affichage dans un menu.

char* menu [] = { "Faire 0", "Faire 1", "Faire 2",..... "Faire 10" };

Un second contient le nom des fonctions :

void fonction [] = { fonction_0 (), fonction_1 (), fonction_2 (),..... fonction_10 () };

Selon le choix de l'utilisateur, par exemple "Faire 1", une variable i prend la valeur de 1, puis sert à appeler la fonction...

Question : que doit contenir la ligne de commande et le tableau fonction[] ?

J'ai fait des essais avec des commandes du genre :
fonction[ i ] , fonction [ i ] (void) , fonction [ i ] ()...

J'ai fait également modifié le tableau "fonction []" sans les (),

J'ai regardé ce lien :

https://openclassrooms.com/forum/sujet/arduino-tableau-de-fonction

Mais pour l'instant rien, je n'arrive pas à faire fonctionner cette partie de mon programme. :confused:

Merci pour votre aide, :slight_smile:

Amicalement,

J-F

Re,

J'ai continué mes essais en m'aidant avec l'exemple du fil que je cite.

J'utilise un tableau du style destiné à l'appel de la fonction choisie :

void (*fct_menu)(void) = {"regHeure", "regDate", "thermoCons", "hygroCons", "razminmax", "razdpr"};

mais le compilateur m'indique le message d'erreur suivant :

xxxxxxx.ino:655:136 : errors : scalar object 'fct_menu' requires one element in initializer

Je suppose qu'il manque un pointeur, non ?

J'ai des grosses lacunes en C++ :confused:

Amicalement,

J-F

Bonjour,

Il me semble qu'il manque les crochets : void (fct_menu*[]**) ?
Amicalement
Obelux

Bonjour,

Il faut bien comprendre que les noms des fonctions ont disparus à l’exécution, donc tu ne peux pas les appeler avec une chaine de caractères
Il faut que tu déclares un tableau d'adresses (pointeurs) de ces fonctions.

void (*fct_menu[])(void) = {regHeure, regDate, thermoCons, hygroCons, razminmax, razdpr};

void setup() {
  // put your setup code here, to run once:
  fct_menu[1]();  // appel de redDate
}

void loop() {
  // put your main code here, to run repeatedly:

}

void regHeure()
{
}
void regDate()
{
}
void thermoCons()
{
}
void hygroCons()
{
}
void razminmax()
{
}
void razdpr()
{
}

le café n'est pas vraiment la bonne section pour poser une question technique, même "si ce n'est que du code"...

Un grand merci pour vos réponses.

Merci Obelux pour ta remarque.

Kamill, je vais essayer de bien comprendre ta réponse afin de la mettre en œuvre.

Zorro, à quel endroit est il préférable de déposer ce genre de question ?

Bon je retourne à l'ouvrage et vous tiens au courant.

Je vous souhaite un bon week-end.

Amicalement,

J-F

Le meilleur endroit c'est le forum technique "normal" : Français - Arduino Forum

Sinon, concernant les pointeurs sur fonction, il est parfois plus simple (du point de vue de la lisibilité) de se faire un "typedef" pour ce genre de déclarations :

typedef void (*pFonction)(void);

pfonction tableauDeFonctions[] = {regHeure, regDate, thermoCons, hygroCons, razminmax, razdpr};

void setup() {
  // put your setup code here, to run once:
  tableauDeFonctions[1]();  // appel de redDate
}

void loop() {
  // put your main code here, to run repeatedly:

}

void regHeure()
{
}
void regDate()
{
}
void thermoCons()
{
}
void hygroCons()
{
}
void razminmax()
{
}
void razdpr()
{
}

Edit1: c'est bon, le sujet à été déplacé dans la bonne section par les administrateurs.
Edit2: Si tu as absolument besoin d'un code qui appelle une fonction d'après un nom donné sous forme de chaine de caractères (un interpréteur de commandes par exemple), la manip sera différente : il te faudra comparer la chaîne à interpréter avec un tableau de chaînes pour identifier la fonction à appeler. Ca ne se fait pas automatiquement, c'est à toi de te le coder...

Bonjour,

Je vous adresse un grand merci pour votre aide.

Grace à vous j'ai résolu ce problème.

La réponse de Kamill m'a permis de comprendre mes erreurs.

Merci Zorro_X pour tes explications, je ne connaissais pas la commande "typedef" pour simplifier le code.

J'apprend la programmation arduino, mon niveau en C++ est vraiment insuffisant, mais j'essai ( à mon niveau) de programmer "proprement".

Je présente mes excuses aux administrateurs et les remercie d'avoir déplacé mon post.

Amicalement,

J-F

Juste pour info, pourquoi as tu besoin d'un tableau de pointeur sur fonction ?

Même si ce principe est super intéressant à étudier, dans la majorité des cas cette façon d'écrire rend plus difficile la compréhension du code qu'autre chose. Car si finalement, tu es obligé de faire un switch ou des if sur l'index de ton tableau, autant appeler la fonction directement.

Bonjour Vohu,

Merci pour ton message.

Je désire associer deux listes, une liste de choix (char* txt_menu2), associée à une liste de fonctions (void (*fct_menu[])(void)) .

La liste de choix s'affiche sur cinq boutons d'un menu arborescent.

Par exemple, au démarrage de la routine, les cinq boutons affichent les données de 0 à 4 soit : Réglages, R.A.Z, Divers, " ", et Annulation.

Un appui sur Réglages, remplace l'affichage sur ces boutons par la liste suivante dans le tableau, données de 5 à 9, soit Heure, Date, Thermostat, Hygrostat, et Annulation. Si le choix est R.A.Z ce sont les données 10 à 14 qui s'affichent...

Un nouvel appui sur un des cinq boutons lance la fonction, par exemple, bouton 2 = réglage de la Date.

char* txt_menu2 [] = {"Reglages", "R.A.Z", "Divers", " ", "Annulation",                   // pointeur cx2        
                        "Heure", "Date", "Thermostat", "Hygrostat", "Annulation",        
                        "Mini. / Max.", "Points roses", " ", " ", "Annulation", 
                        "Verrou. 12 h", " ", " ", " ", "Annulation",};
/*
void (*fct_menu[])(void) = {regHeure, regDate, thermoCons, hygroCons, razminmax, razdpr,};  // pointeur cx3

Bon ce n'est peut être pas une bonne solution, je m'inspire de l'exemple que je cite au début de ce fil.

Le but, c'est de faire un menu arborescent, sur un LCD tactile 320 x 240, je n'utilise que l'écran pour l'ensemble des commandes.

L'utilisation de Case / Switch serait peut être plus judicieux ?

Je débute dans la programmation, et puis l'âge :confused:

Amicalement,

J-F

Ayant le contexte, je pense que ton approche est sympa.

Apres, plutôt que d'utiliser 2 tableaux (1 pour le texte et 1 pour les fonctions), et afin d'associer le texte du menu à sa fonction, j'aurai créé un tableau d'une structure contenant une chaine et un pointeur sur fonction.

typedef struct {
const char* text;
void (*callback)(void);
} Menu;

(Dsl suis sur mon téléphone, c'est assez pénible pour écrire ce genre de choses :o)

Merci beaucoup Vohu pour ton message et ton mérite à écrire avec le téléphone. :slight_smile:

Demain soir je vais étudier ton explication afin de faire un essai.

Bonne fin de soirée.

Amicalement,

J-F

Je m'en voulais un peu de ma réponse furtive hier.

Voilà un petit exemple d'utilisation des pointeurs sur fonction (une version en ligne de comande, sans arduino)

// pntfonction.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


// déclaration de la structure
typedef struct Menu
{
    char texte[15];
    void ( *callback ) ( void );
} Menu;
// initialisation d'un tableau de 3 menus
Menu menu[3];

// fonctions servant de callback au menu
void fichier ( void )
{
    printf ( "menu fichier\n" );
}
void edition ( void )
{
    printf ( "menu édition\n" );
}
void affichage ( void )
{
    printf ( "menu affichage\n" );
}

// fonction fabriquant le menu
void prepare_menu ( void )
{
    strcpy ( menu[0].texte, "Fcihier" );
    menu[0].callback = fichier;

    strcpy ( menu[1].texte, "Edition" );
    menu[1].callback = edition;

    strcpy ( menu[2].texte, "Affichage" );
    menu[2].callback = affichage;
}


// programme rapide de test
// gcc -o pntfonction pntfonction.c
// utilisation : pntfonction 1
int main ( int argc, char *argv[] )
{

    int index = atoi ( argv[1] );
    if ( argc < 2 || index < 0 || index > 2 )
    {
        printf ( "erreur : 1 arguement manquant ou hors limites\n" );
        exit ( EXIT_FAILURE );
    }

    prepare_menu (  );

    menu[index].callback (  );

    return 0;
}

Merci beaucoup Vohu pour ton message,

Je vais essayé de comprendre le contenu du code de ton message.

Ce code contient des commandes que je ne connais pas, heureusement il y a Google :slight_smile:

Je ne connaissais pas de ce fait les librairies stdlib.h, et stdio.h.

Pour l'instant mon menu n'est pas optimisé, il fonctionne à grand renfort de if / else...

J'ai acheté le livre " Programmez avec le langage C++" de chez OpenClassroms, j'espère faire des progrès grâce à ce livre.

Amicalement,

J-F

Ok, attention mon code est en C pas en C++.

Mon code en vrai C++ ferait des includes de iostream et std. J'aurais utilisé cout au lieu de printf.
J'aurais aussi plutôt créé une classe avec les bonnes méthodes plutôt que manipuler une structure (ce qui est aussi valable en C++).

Il s'agit bien de 2 langages différents malgré que c++ maintienne une rétrocompatibilité avec le c.

Merci Vohu pour cette information.

Pour l'Arduino faut il mieux apprendre le C plutôt que le C++ ?

D'après mes lectures le langage Arduino semble être un peu un "mélange" de C et de programmation orientée objet.

Mais j'avoue que pour l'instant c'est assez flou pour moi.

Bonne soirée,

J-F

Mis à part apprendre les deux (ce que je n'ai pas fait, car j'estime avoir encore du boulot sur le c) je ne te conseillerais pas sur le choix de langage, c'est quelque chose qu'il vaut mieux faire par soit même.

Par contre je te conseille d'éviter de mélanger les deux sans comprendre ce que tu fais.

D'autres pourraient très bien te dire le contraire... Ce sont des débats sans fin, et dépourvus d'intérêt si l'on ne possède pas la maîtrise parfaite de ces langages...

Je suis d'accord avec toi, j'ai déjà des difficultés à assimiler un langage.

Cet apprentissage est exclusivement destiné à utiliser les cartes Arduino.

Alors c'est à moi de réfléchir à celui qui me conviendra le mieux. :slight_smile:

Sur arduino c'est un compilateur C++ qui est utilisé. Le fait de pouvoir faire du C en découle parcequ'en C++ c'est un peu comme "qui peut le plus, peut le moins"... Sur Arduino tu manipules la plupart du temps des objets C++ lorsque t'utilises les librairies incluses, l'objet Serial par exemple pour communiquer sur ton lien série avec ton ordinateur depuis l'Arduino est un objet C++. La librairie LiquidCrystal pour manipuler les écrans LCD en est un autre, et ainsi de suite pour la plupart des librairies que t'importeras ou utiliseras.

Les deux langages sont très utiles et pratiques. En C lorsque tu commences à vouloir "bien coder" tu finis par faire de l'objet d'une façon détournée en utilisant des structs et des pointeurs sur fonction... Ce que le C++ fait déjà très bien et d'une façon plus claire, avec des règles claires elles aussi.

L'avantage du C c'est que tu est "plus proche" de ce qui se passe vraiment sur ton µC sans pour autant coder en assembleur...

Enfin, l'avantage sur Arduino c'est que tu n'as pas besoin de comprendre le C++ pour en faire. Comprendre le C, qui à priori est plus simple, "suffit" pour faire la plupart des choses.
Sur OC t'as un cours sur le C qui déjà dans les deux premières parties t'apporte des bases solides pour pouvoir "faire de l'Arduino" sans être perdu. D'après leur dires, ca prendrait dans les 2h de lire ces deux chapitres, je trouve que c'est un excellent rapport temps/résultat pour bien commencer.

Merci beaucoup Zorro_X pour ton explication très complète et pour les liens.

Je ne connaissais pas le site de DiegoYourself, les exemples sont vraiment didactiques.

J'ai regardé également le lien vers les cours sur le C, les leçons y sont présentées comme celles du livre sur le C++ que j'ai acheté.

La première moitié de ce livre jusqu'au chapitre 17 est intéressante, la seconde moitié du livre aborde des sujets inutiles pour l'arduino. Pour l'instant j'en suis au premiers chapitres.

Un petit mot sur mon projet :

Le montage est basé sur un arduino mega 2560, un capteur I2C AM 2315 (hygromètre et thermomètre), une horloge DS1307 l'interface repose sur un afficheur ITEAD LCD tactile 3,2" de 240 x 320 pixels.

J'utilise les librairies de Rinky-Dink pour la gestion de l'écran.

Deux relais sont prévus pour piloter un chauffage et un assécheur d'air.

Le programme calcule le point de rosée et fait fonction d'alarme lors de l'atteinte de ce point.

Au déclenchement de l'alarme, un compteur est incrémenté, et un verrouillage de 12 heures empêche un nouvel incrément du compteur.

Il affiche les valeurs instantanées de t et de l'humidité relative.

Il affiche également les mini / max de t et de hr de 60 mesures moyennées sur une période de 10 minutes.

Deux Leds indiquent le "collage" des relais, une troisième signale "alarme point de rosée atteint", la dernière indique toutes les 10 secondes qu'une mesure est en cours.

Par la suite je désire ajouter une mémoire type 24LC... ou SD card pour enregistrer les mesures.

Mais bon, vu mon niveau je progresse pas à pas, quand un élément fonctionne j'en ajoute un autre afin de me familiariser à un seul élément à la fois.

Je tiens à vous remercier tous pour votre aide vraiment utile. :slight_smile:

Amicalement,

J-F