Simple text menu in C++

谁都会走 提交于 2020-01-14 05:54:46

问题


I am writing a silly little app in C++ to test one of my libraries. I would like the app to display a list of commands to the user, allow the user to type a command, and then execute the action associated with that command. Sounds simple enough. In C# I would end up writing a list/map of commands like so:

    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)));
    }

Any suggestions on how to achieve this in C++? I don't really want to define separate classes/functions for each command. I can use Boost, but not TR1.


回答1:


A very common technique is to use function pointers, or boost::function, indexed by the item name, or by having a vector of them and indexing by the item index for this job. Simple example using the item name:

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++ doesn't have a lambda feature yet, so you really have to use functions for this task, sadly. You can use boost::lambda, but note it is just simulating lambdas, and nowhere near as powerful as a native solution:

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

Note the use of constant(...), since otherwise boost::lambda wouldn't notice that this is supposed to be a lambda expression: The compiler would try to output the string using std::cout, and assign the result (an std::ostream reference) to menu["help"]. You can still use boost::function, since it will accept everything returning void and taking no arguments - including function objects, which is what boost::lambda creates.

If you really don't want separate functions or boost::lambda, you can just take print out a vector of the item names, and then switch on the item number given by the user. This is probably the easiest and most straight forward way of doing it.




回答2:


Why not just port the C# code to C++? There's a little work to be done, but something like this should get most of your work done:

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;
}

With that defined, your main() can use a std::list, and use a simple while() loop that checks the exit value of the MenuItem's Action to determine if it should exit.



来源:https://stackoverflow.com/questions/290484/simple-text-menu-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!