Je souhaite appeler une fonction qui affiche un menu avec quelque chose comme ceci:
Mode_Menu ("Item 1", "Item A1c", "Item Fred", ..... "Item xxx ")
où n
pourrait être n'importe quel nombre raisonnable et chaque élément peut être de longueur aléatoire.
J'ai essayé ce qui suit (ignorez les nombres dans Offer_Mode
- c'est juste la coordonnée Y
sur l'écran LCD)
Item 1 tem 1 em 1 m 1 1
Mais ce que j'obtiens, c'est
void Mode_Menu(char *m1, ...) { va_list argp; va_start(argp, *m1); Offer_Mode(2, m1++); Offer_Mode(33, m1++); Offer_Mode(64, *m1++; Offer_Mode(97, *m1++); Offer_Mode(130, *m1++); }
ie le pointeur se déplace le long du premier élément et ne voit même jamais le deuxième élément dans l'appel de fonction.
J'ai essayé toutes les incantations auxquelles je peux penser pour avancer vers les éléments suivants en utilisant des choses comme * m1 []
ou m1 [2]
dans la définition de la fonction et dans va_start
mais tout ce que j'essaye génère des erreurs.
Quelqu'un peut-il m'indiquer la bonne façon de procéder s'il vous plaît?
(et j'ai d'abord effectué une recherche approfondie, alors veuillez ne pas marquer comme doublon. Il y a beaucoup d'exemples utilisant des entiers mais aucun que je puisse trouver en utilisant des tableaux de caractères).
3 Réponses :
L'interface C pour les modèles vardiac le rend:
template <typename ...T> std::enable_if_t<(std::is_same_v<T, char>&&...)> Mode_Menu(const T*... args) { const char * texts[] = { args... }; Offer_Mode(2, texts[0]); Offer_Mode(33, texts[1]); Offer_Mode(64, texts[2]); .... }
EDIT: Le code réel devrait avoir un moyen de trouver la fin des paramètres. Cela peut être par un premier argument entier, à partir d'informations de chaîne (comme le format printf, ou par correspondance de motif dans les chaînes), ou par une sentinelle nullptr (dernier paramètre).
La méthode C ++ (avec SFINAE pour vérifier l'exactitude du type)
void Mode_Menu(char *arg, ...) { va_list argp; va_start(argp, &arg); Offer_Mode(2, arg); arg=va_arg(argp, char*) Offer_Mode(33, arg); arg=va_arg(argp, char*) Offer_Mode(64, arg); .... va_end(argp); }
EDIT 2: La variante de modèle contient déjà des informations de taille, toutes deux en sizeof ... (T)
et std :: size (textes)
. Pour cette raison, contrairement à la variante C, il n'est pas nécessaire de travailler dur pour détecter la dernière chaîne.
Comment vérifier la taille du tableau textes
? Dans votre exemple, s'il n'y a qu'un seul argument, vous allez avoir une erreur au niveau de textes [1]
.
@Pierre il n'y a pas un seul argument. C'est une extension de pack de paramètres, avec autant de paramètres qu'il y en a dans l'entrée. Vous pouvez vérifier leur nombre au moment de la compilation avec sizeof ... (T), ou std: size (textes)
Voir en.cppreference.com/w/cpp/language/… a>
Variante de fonction variadique: il faut mentionner que nous avons encore besoin d'un moyen de trouver la fin de la liste d'arguments. Ma variante personnelle serait de terminer les arguments avec un pointeur nul comme sentinelle ...
@Aconcagua pour la variante C, je suis tout à fait d'accord mais cela ne faisait pas partie de la question
Cela ne faisait probablement pas partie de la question, car OP n'était pas au courant du problème, alors nous la laisserions tomber aveuglément dans le prochain problème si nous nous taisions ...
Je répète ce que j'ai déjà dit en commentaire:
En C ++, je préférerais des modèles variadiques ou, peut-être, fournir des arguments dans un conteneur (comme par exemple un std :: vector). Si ce n'est pas du tout une option, j'utiliserais va_arg pour accéder aux arguments. Et, s'il vous plaît, n'oubliez pas le va_end (). Le traitement des arguments variadiques dans les macros va_ peut être très dépendant de la plate-forme et fragile s'il n'est pas utilisé avec précaution.
Cependant, (juste en me souvenant de mes propres tentatives de ressembler à printf ()
dans le loin au-delà) un échantillon:
[1]: Item 1 [2]: Item A1c [3]: Item Fred
Sortie:
#include <iostream> #include <cstdarg> void printMenu(int len, ...) { va_list argp; va_start(argp, len); for (int i = 0; i < len; ++i) { const char *item = va_arg(argp, const char*); std::cout << '[' << (i + 1) << "]: " << item << '\n'; } va_end(argp); } int main() { printMenu(3, "Item 1", "Item A1c", "Item Fred"); return 0; }
Remarques: p >
Une difficulté est de reconnaître correctement le nombre d'arguments variadiques. J'ai utilisé un décompte pour cela. Une autre option souvent utilisée est de remarquer la fin (par exemple avec un argument nullptr
). Concernant printf ()
, le nombre d'arguments attendus dépend directement du contenu de l'argument du formateur. (Par conséquent, les printf ()
sont considérés comme fragiles et dangereux.)
Seuls certains types peuvent être attendus pour les arguments. Pour en savoir plus, cliquez ici: Arguments variadiques - Conversions par défaut
D'accord sur la fin - je pense que les musiciens comprendront ce que signifie "Fin du menu" et le laisseront là :-)
Vous pouvez résoudre en spécifiant le nombre d'arguments supplémentaires que vous passez.
Chaque argument supplémentaire doit être "extrait" de la liste.
J'ai utilisé cout
parce que je je ne sais pas ce que fait Offer_mode
.
#include<iostream> #include <stdarg.h> void Mode_Menu(char *m1, unsigned int count,...); int main(int argc, char* argv[]) { Mode_Menu("Item 1", 3,"Item A1c", "Item Fred", "Item xxx"); return 0; } void Mode_Menu(char *m1, unsigned int count, ...) { va_list ap; va_start(ap, count); /* Requires the last fixed parameter (to get the address) */ for (int j = 0; j < count; j++) { std::cout<<va_arg(ap, char*)<<std::endl; //ap automatically incremented } va_end(ap); }
Vous avez oublié d'utiliser va_end
. Pourquoi est-ce important: stackoverflow.com/questions/587128/...
Pourquoi ne pas passer un
std::vector
ou de manière similairestd :: array
à la place?En C ++, je préférerais des modèles variadiques ou, peut-être, fournir des arguments dans un conteneur (comme par exemple un
std :: vector
). Si ce n'est pas du tout une option, j'utiliseraisva_arg code>
pour accéder aux arguments. Et, s'il vous plaît, n'oubliez pas leva_end ()
. Le traitement des arguments variadiques dans les macros va_ peut être très dépendant de la plate-forme et fragile s'il n'est pas utilisé avec précaution.Actuellement, vous n'utilisez tout simplement pas du tout les arguments variadiques ... Vous aurez besoin de va_arg pour (au lieu d'incrémenter le premier argument du pointeur). Sachez que vous devez trouver un moyen d'obtenir vous-même la fin de la liste d'arguments. Vous devriez éviter les fonctions variadiques (classiques) (comme @Scheff le recommande déjà), cependant, elles sont assez dangereuses.
Un conteneur n'est pas une option car la ligne de menu doit être éditée directement par des personnes qui ne sont pas des experts C ++ (alias musiciens). J'ai besoin de les isoler de toutes les complexités du langage. Mais oui, c'était varg qui me manquait. Merci à Scheff et à l'Aconcagua