2008-11-14 10 views
0

J'écris une petite application stupide en C++ pour tester une de mes bibliothèques. Je voudrais que l'application affiche une liste de commandes à l'utilisateur, permette à l'utilisateur de taper une commande, puis exécute l'action associée à cette commande. Cela semble assez simple. En C# Je finirais par écrit une liste/carte de commandes comme ceci:Menu texte simple en C++

class MenuItem 
    { 
     public MenuItem(string cmd, string desc, Action action) 
     { 
      Command = cmd; 
      Description = desc; 
      Action = action; 
     } 

     public string Command { get; private set; } 
     public string Description { get; private set; } 
     public Action Action { get; private set; } 
    } 

    static void Main(string[] args) 
    { 
     var items = new List<MenuItem>(); 

     items.Add(new MenuItem(
      "add", 
      "Adds 1 and 2", 
      ()=> Console.WriteLine(1+2))); 
    } 

Toutes les suggestions sur la façon d'y parvenir en C++? Je ne veux pas vraiment définir des classes/fonctions séparées pour chaque commande. Je peux utiliser Boost, mais pas TR1.

Répondre

6

Une technique très courante consiste à utiliser des pointeurs de fonction, ou boost :: function, indexés par le nom de l'élément, ou en ayant un vecteur de ceux-ci et indexés par l'index des éléments pour ce travail. Exemple simple en utilisant le nom de l'élément:

void exit_me(); /* exits the program */ 
void help(); /* displays help */ 

std::map< std::string, boost::function<void()> > menu; 
menu["exit"] = &exit_me; 
menu["help"] = &help; 

std::string choice; 
for(;;) { 
    std::cout << "Please choose: \n"; 
    std::map<std::string, boost::function<void()> >::iterator it = menu.begin(); 
    while(it != menu.end()) { 
     std::cout << (it++)->first << std::endl; 
    } 

    std::cin >> choice; 
    if(menu.find(choice) == menu.end()) { 
     /* item isn't found */ 
     continue; /* next round */ 
    } 

    menu[choice](); /* executes the function */ 
} 

C++ n'avez pas encore une fonction lambda, donc vous vraiment utiliser des fonctions pour cette tâche, malheureusement. Vous pouvez utiliser boost :: lambda, mais notez qu'il est juste simulait lambdas, et loin d'être aussi puissant que d'une solution native:

menu["help"] = cout << constant("This is my little program, you can use it really nicely"); 

Notez l'utilisation constante (...), sinon boost :: lambda ne remarquerait pas que c'est supposé être une expression lambda: le compilateur essaierait de sortir la chaîne en utilisant std :: cout, et assignerait le résultat (une référence std :: ostream) au menu ["help"]. Vous pouvez toujours utiliser boost :: function, car il acceptera tout retournant void et ne prenant aucun argument - y compris les objets de fonction, ce que boost :: lambda crée.

Si vous ne voulez pas vraiment de fonctions séparées ou boost :: lambda, vous pouvez simplement imprimer un vecteur des noms d'éléments, puis switch sur le numéro d'article donné par l'utilisateur. C'est probablement le moyen le plus simple et le plus simple de le faire. Pourquoi ne pas simplement porter le code C# en C++?

+0

Fermer, mais je dois encore définir des fonctions distinctes exit_me() et aide() ... Y at-il une autre façon? –

+0

Merci. Je vais rester en utilisant des fonctions séparées et attendre que les lambdas de TR1 soient incorporés dans la norme. –

+0

lambdas sera une nouvelle fonctionnalité du prochain C++. vous pourrez faire le menu ["help"] = []() {cout << "ceci est ma petite aide"; } puis. –

0

Il y a un peu de travail à faire, mais quelque chose comme cela devrait obtenir la plupart de votre travail:

using std::string; 
class MenuItem  
{   
    public: 
     MenuItem(string cmd, string desc, boost::function<bool()> action):Command(cmd), 
                      Description(desc), 
                      Action(action) 
     {} 
     boost::function<bool()> GetAction() { return Action; } 
     string GetDescription() { return Description; } 
     string GetCommand() { return Command; } 
    private: 
     string Command; 
     string Description; 
     boost::function<bool()> Action; 
} 

Avec cette définition, votre principal peut utiliser un std :: list() et utiliser un simple while() boucle qui vérifie la valeur de sortie de l'action MenuItem pour déterminer si elle doit quitter.

+0

Le MenuItem n'est pas le problème ici. C'est le manque de lambdas ou de fermetures. Dans le C++ d'aujourd'hui, je dois définir des fonctions séparées pour chacune de mes commandes (boost :: function leur "pointe"), au lieu de tout faire en un seul endroit. Voir les commentaires de litb. –