What's an effective way to parse command line

2019-04-08 15:19发布

Is there a really effective way of dealing with command line parameters in C++?

What I'm doing below feels completely amateurish, and I can't imagine this is how command line parameters are really handled (atoi, hard-coded argc checks) in professional software.

// Command line usage: sum num1 num2

int main(int argc, char *argv[])
{
   if (argc < 3)
   {
      cout << "Usage: " << argv[0] << " num1 num2\n";
      exit(1);
   }
   int a = atoi(argv[1]);    int b = atoi(argv[2]);    int sum = a + b;
   cout << "Sum: " << sum << "\n";
   return 0; }

6条回答
ゆ 、 Hurt°
2楼-- · 2019-04-08 15:24

It's a bit too big to include in a Stack Overflow answer, but I made a library for defining commands lines declaratively. It takes advantage of the the C++14 ability to build up a class constructor by giving initial values to each member variable.

The library is mostly a base class. To define your command syntax, you declare a struct that derives from it. Here's a sample:

struct MyCommandLine : public core::CommandLine {
    Argument<std::string> m_verb{this, "program", "program.exe",
        "this is what my program does"};
    Option<bool> m_help{this, "help", false,
        "displays information about the command line"};
    Alias<bool> alias_help{this, '?', &m_help};
    Option<bool> m_demo{this, "demo", false,
        "runs my program in demonstration mode"};
    Option<bool> m_maximize{this, "maximize", false,
        "opens the main window maximized"};
    Option<int> m_loops{this, "loops", 1,
        "specifies the number of times to repeat"};
    EnumOption<int> m_size{this, "size", 3,
                           { {"s", 1},
                             {"small", 1},
                             {"m", 3},
                             {"med", 3},
                             {"medium", 3},
                             {"l", 5},
                             {"large", 5} } };
    BeginOptionalArguments here{this};
    Argument<std::string> m_file{this, "file-name", "",
        "name of an existing file to open"};
} cl;

The Argument, Option, and Alias class templates are declared in the scope of the CommandLine base class, and you can specialize them for your own types. Each one takes the this pointer, the option name, the default value, and a description for use in printing the command synopsis/usage.

I'm still looking to eliminate the need to sprinkle all the this pointers in there, but I haven't found a way to do it without introducing macros. Those pointers allow each member to register itself with the tables in the base class that drive the parsing.

Once you have an instance, there are several overloads of a method to parse the input from a string or a main-style argument vector. The parser handles both Windows-style and Unix-style option syntax.

if (!cl.Parse(argc, argv)) {
    std::string message;
    for (const auto &error : cl.GetErrors()) {
        message += error + "\n";
    }
    std::cerr << message;
    exit(EXIT_FAILURE);
}

Once it's parsed, you can access the value of any of the options using operator():

if (cl.m_help()) { std::cout << cl.GetUsage(); }
for (int i = 0; i < cl.m_loops(); ++i) { ... }

The whole library is only about 300 lines (excluding tests). The instances are a bit bloaty, since the parsing tables are part of the instance (rather than the class). But you generally only need one instance per program, and the convenience of this purely declarative approach is pretty powerful, and an instance can be reset simply by parsing new input.

查看更多
迷人小祖宗
3楼-- · 2019-04-08 15:26

You could use an already created library for this

http://www.boost.org/doc/libs/1_44_0/doc/html/program_options.html

查看更多
Summer. ? 凉城
4楼-- · 2019-04-08 15:30

I am using getopt() under windows/mingw :

while ((c = getopt(myargc, myargv, "vp:d:rcx")) != -1) {
        switch (c) {
        case 'v': // print version
            printf("%s Version %s\n", myargv[0], VERSION);
            exit(0);
            break;
        case 'p': // change local port to listen to
            strncpy(g_portnum, optarg, 10);
            break;
...
查看更多
等我变得足够好
5楼-- · 2019-04-08 15:37

if this is linux/unix then the standard one to use is gnu getopt

http://www.gnu.org/s/libc/manual/html_node/Getopt.html

查看更多
小情绪 Triste *
6楼-- · 2019-04-08 15:40

You probably want to use an external library for that. There are many to chose from.

Boost has a very feature-rich (as usual) library Boost Program Options.

My personal favorite for the last few years has been TCLAP -- purely templated, hence no library or linking, automated '--help' generation and other goodies. See the simplest example from the docs.

查看更多
爷的心禁止访问
7楼-- · 2019-04-08 15:44

I would recommend always using boost lexical_cast<> in place of junk like atoi, atof, etc.

http://www.boost.org/doc/libs/release/libs/conversion/lexical_cast.htm

Other than that your code okay for simple stuff.

查看更多
登录 后发表回答