211

What is the best way of parsing command-line arguments in C++ if the program is specified to be run like this:

prog [-abc] [input [output]]

Is there some way of doing this built into the standard library, or do I need to write my own code?


Related:

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Verhogen
  • 27,221
  • 34
  • 90
  • 109
  • I think the Nunit source code (C#) has a good example of a command line handling class.... – Mitch Wheat Sep 30 '10 at 00:35
  • 1
    The easiest would be to use one of the argument parsing libraries: `getopt` or `argparse`. – Andrejs Cainikovs Sep 15 '16 at 07:12
  • If you can't use libraries (e.g. boost), at least use `std::vector args(argv, argv+argc);` so you can parse a vector of strings instead of an array of char-arrays. – stefaanv Sep 15 '16 at 07:45
  • 1
    For people using OpenCV already in their program, cv::CommandLineParser may also be a good choice. [ I mean just in case you are using it already for other purpose, I don't mean to include OpenCV for command line parser.] – Dharma Mar 03 '18 at 05:04
  • 1
    Recently wrote this for modern c++: https://github.com/juzzlin/Argengine – juzzlin Mar 02 '20 at 17:00
  • chechout https://github.com/p-ranav/argparse – droptop Jan 25 '21 at 08:51

42 Answers42

268

The suggestions for boost::program_options and GNU getopt are good ones.

However, for simple command line options I tend to use std::find

For example, to read the name of a file after a -f command line argument. You can also just detect if a single-word option has been passed in like -h for help.

#include <algorithm>

char* getCmdOption(char ** begin, char ** end, const std::string & option)
{
    char ** itr = std::find(begin, end, option);
    if (itr != end && ++itr != end)
    {
        return *itr;
    }
    return 0;
}

bool cmdOptionExists(char** begin, char** end, const std::string& option)
{
    return std::find(begin, end, option) != end;
}

int main(int argc, char * argv[])
{
    if(cmdOptionExists(argv, argv+argc, "-h"))
    {
        // Do stuff
    }

    char * filename = getCmdOption(argv, argv + argc, "-f");

    if (filename)
    {
        // Do interesting things
        // ...
    }

    return 0;
}

On thing to look out for with this approach you must use std::strings as the value for std::find otherwise the equality check is performed on the pointer values.


I hope it is okay to edit this response instead adding a new one, as this is based on the original answer. I re-wrote the functions slightly and encapsulated them in a class, so here is the code. I thought it might be practical to use it that way as well:

class InputParser{
    public:
        InputParser (int &argc, char **argv){
            for (int i=1; i < argc; ++i)
                this->tokens.push_back(std::string(argv[i]));
        }
        /// @author iain
        const std::string& getCmdOption(const std::string &option) const{
            std::vector<std::string>::const_iterator itr;
            itr =  std::find(this->tokens.begin(), this->tokens.end(), option);
            if (itr != this->tokens.end() && ++itr != this->tokens.end()){
                return *itr;
            }
            static const std::string empty_string("");
            return empty_string;
        }
        /// @author iain
        bool cmdOptionExists(const std::string &option) const{
            return std::find(this->tokens.begin(), this->tokens.end(), option)
                   != this->tokens.end();
        }
    private:
        std::vector <std::string> tokens;
};

int main(int argc, char **argv){
    InputParser input(argc, argv);
    if(input.cmdOptionExists("-h")){
        // Do stuff
    }
    const std::string &filename = input.getCmdOption("-f");
    if (!filename.empty()){
        // Do interesting things ...
    }
    return 0;
}
0x90
  • 39,472
  • 36
  • 165
  • 245
iain
  • 10,798
  • 3
  • 37
  • 41
  • I Rick you are correct about the #include thanks for updating my answer – iain Jan 19 '12 at 15:30
  • 1
    Just a question, do i need to do anything or does this work out of the box? You write to look out for : std::strings - so should i convert char* to a std::string anywhere or ? – Poul K. Sørensen Mar 15 '12 at 13:56
  • 5
    This works out of the box. However note that the option parameter is `const std::string&`. It is important that the value parameter to `std::find` is a `std::string` so that `std::string::operator==()` is used not the `char * operator==()` (as the latter will only compare the pointer value and not the string contents). – iain Mar 26 '12 at 09:49
  • 7
    This doesn't work as expected, for instance, like the tar application: `tar -xf file`, right? Each option must be separated. `grep -ln pattern file` wouldn't be understood, but would have to be `grep -l -n pattern file`. – lmat - Reinstate Monica Jul 29 '14 at 18:33
  • 11
    Absolutely if you want posix style command line options then you should use one of the command line processing libraries mentioned in other answers. As this answer says this is for simple command line options. – iain Jul 31 '14 at 08:04
  • 4
    This is nice, but two minor improvements were needed: First, constructor params should be const-qualified, second, return value of getCmdOption should be a value, not a reference, otherwise you run into http://stackoverflow.com/questions/1339601/warning-returning-reference-to-temporary. Other than that, a nice and simple solution, I'll use that, thanks. – Tomáš Dvořák Jun 28 '16 at 20:34
  • An important note: This class does not compile as is with Visual Studio compiler (I'm using 2010 version). You must explicitly include header to your program (iostream and vector are not sufficient). Got this solution from : http://stackoverflow.com/questions/9080028/vector-equals-for-type-stdstring-works-in-g-but-not-in-visual-studio-2010 – AamodG Jul 05 '16 at 06:38
  • 3
    1. @iain What is the license of that piece of code? 2. @TomášDvořák is right about the return reference to a temporary, when `getCmdOption` returns the empty string. `InputParser` should have a `std::string empty_string` as a member, and return its reference when the option is not found. – lrineau Jan 25 '17 at 10:30
  • 2
    The first piece of code is mine the second was a edit by someone else. Regarding my code there is no licence it is public domain, feel free to use as you like without any warranty. I am not sure about the second. Regarding the InputParser class I am happy that it was contributed to my answer, but I would not put the args into a vector, but store `argv` and `argv + argc` as the `begin` and `end` iterators. – iain Jan 26 '17 at 10:54
  • @iain I am getting an error: `no instance of function template "std::find" matches the argument list argument types are: (__gnu_cxx::__normal_iterator>>, __gnu_cxx::__normal_iterator>>, const std::__cxx11::string)` – Vsevolod A. Oct 05 '18 at 19:22
  • I am not sure why. I haven't written any c++ in years. From the error you are getting it looks like you are using the code at the bottom added by someone else. Sorry I cannot be more help, but I do not even have a c++ compiler installed that I could test this out with. – iain Oct 08 '18 at 11:01
90

Boost.Program_options should do the trick

ChrisN
  • 16,635
  • 9
  • 57
  • 81
  • 16
    Good choice. Alternatively, if you can't use boost for some reason then the standard c based "getopt" function will also get the job done. – hookenz Jul 16 '09 at 02:47
  • 12
    The documentation for boost::program_options could be more complete. It is specially difficult to find out how to use files to keep the options, a critical feature. – gatopeich Jun 16 '11 at 10:59
  • 83
    introducing boost to a code base just to parse command line options is a bit "sledgehammer to crack a nut". If boost is there already use it. Otherwise have a look at something like gopt. Nothing against boost in general but its kinda heavyweight and i find that the versions are tied tightly to g++ versions. – Stephen Mar 30 '14 at 17:59
  • 28
    Furthermore, boost::program_options is not a header only library. You'll have to build boost. This is very troublesome. – ABCD Mar 24 '15 at 07:21
  • 9
    boost seems like a total overkill for this task –  Mar 02 '17 at 20:33
  • 1
    This answer is crying out for a simple example IMHO. – kraxor Oct 30 '18 at 06:27
  • https://www.reddit.com/r/cpp/comments/4zhm2n/which_library_would_you_recommend_for_parsing/ – solstice333 Jun 27 '20 at 12:26
64

I can suggest Templatized C++ Command Line Parser Library (some forks on GitHub are available), the API is very straightforward and (cited from the site):

the library is implemented entirely in header files making it easy to use and distribute with other software. It is licensed under the MIT License for worry free distribution.

This is an example from the manual, colored here for simplicity:

#include <string>
#include <iostream>
#include <algorithm>
#include <tclap/CmdLine.h>

int main(int argc, char** argv)
{

    // Wrap everything in a try block.  Do this every time,
    // because exceptions will be thrown for problems.
    try {

    // Define the command line object, and insert a message
    // that describes the program. The "Command description message"
    // is printed last in the help text. The second argument is the
    // delimiter (usually space) and the last one is the version number.
    // The CmdLine object parses the argv array based on the Arg objects
    // that it contains.
    TCLAP::CmdLine cmd("Command description message", ' ', "0.9");

    // Define a value argument and add it to the command line.
    // A value arg defines a flag and a type of value that it expects,
    // such as "-n Bishop".
    TCLAP::ValueArg<std::string> nameArg("n","name","Name to print",true,"homer","string");

    // Add the argument nameArg to the CmdLine object. The CmdLine object
    // uses this Arg to parse the command line.
    cmd.add( nameArg );

    // Define a switch and add it to the command line.
    // A switch arg is a boolean argument and only defines a flag that
    // indicates true or false.  In this example the SwitchArg adds itself
    // to the CmdLine object as part of the constructor.  This eliminates
    // the need to call the cmd.add() method.  All args have support in
    // their constructors to add themselves directly to the CmdLine object.
    // It doesn't matter which idiom you choose, they accomplish the same thing.
    TCLAP::SwitchArg reverseSwitch("r","reverse","Print name backwards", cmd, false);

    // Parse the argv array.
    cmd.parse( argc, argv );

    // Get the value parsed by each arg.
    std::string name = nameArg.getValue();
    bool reverseName = reverseSwitch.getValue();

    // Do what you intend.
    if ( reverseName )
    {
            std::reverse(name.begin(),name.end());
            std::cout << "My name (spelled backwards) is: " << name << std::endl;
    }
    else
            std::cout << "My name is: " << name << std::endl;


    } catch (TCLAP::ArgException &e)  // catch any exceptions
    { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; }
}
naufraghi
  • 1,514
  • 13
  • 16
  • 3
    This option turned out to be the simplest for me, though it did add a sub-directory to my program with a number of header files. The include paths needed editing accordingly. – Richard May 07 '12 at 22:30
  • 1
    I've used all kinds of solutions over the years, including my own home-rolled one. I join the others in extolling the virtues of TCLAP. it was easy to integrate and answers my needs. – Moshe Rubin Mar 16 '14 at 08:13
  • Looks like the project moved to [sourceforge](http://tclap.sourceforge.net/). – Joe May 12 '14 at 13:08
  • 1
    @JohnShedletsky are you sure? I don't use the lib any more but in the [manual](http://tclap.sourceforge.net/manual.html#EXAMPLE) are showed both long and short format arguments. – naufraghi Sep 28 '15 at 15:20
  • TCLAP works, though its Windows roots are showing. It does compile clean on other platforms, but there are lots of Windowsy default choices: flags are separated from parameters by spaces, not equals signs; flags can require only a short (one-letter) and a long representation; multiple arguments per flag handling is wonky. On the plus side, it works on Mac too, it's not infected with GPL, it's quick to understand, it provides a convenient --help command, and it has no real external dependencies. – johnwbyrd Sep 29 '15 at 00:45
  • @naugraghi Actually it does work but the error I was getting was misleading. – John Shedletsky Sep 29 '15 at 17:57
  • Why does the distribution have a configuration script and a `Makefile` if the library is header-only? – AlwaysLearning Mar 28 '16 at 12:57
  • @AlwaysLearning Opening the `Makefile` it seems to be there to run the tests, compile the docs and build the examples. – naufraghi Mar 29 '16 at 13:19
  • 1
    Didn't vote this one down, but I'm personally not a fan of something small like this having the dependency of exceptions. – Mike Weir Apr 28 '17 at 19:04
  • Was initially reluctant, but noticed it was in the Ubuntu repo which made it extremely easy: `sudo apt-get install libtclap-dev`. Within a short amount of time had replaced my custom argv parsing with calls into TCLAP, including a few custom TCLAP::Constraint classes for validation. Works as advertised, will definitely use it again. Thanks for the link, would never otherwise have heard about it. – Stéphane Aug 26 '19 at 02:19
44

Boost.Program_options

Igor Semenov
  • 1,548
  • 9
  • 7
  • 14
    This seems the most obvious option for C++, but its documentation is not complete enough. Try to find there how to store and retrieve options from a file, an essential feature. I dislike how the code using it looks, specifically the wording `options.add_options()(option1)(option2)...` which I consider an abuse of C++ grammar. – gatopeich Jun 16 '11 at 11:02
  • 8
    Compiling code with Boost.Program_options did not seem straight-forward and required linking options, et cetera, beyond the inclusion of the header file. – Richard May 07 '12 at 19:40
  • 2
    You can get pretty much the same for much less. If you want things like `--long-option`, it's fairly straightforward to do yourself. – Luis Machuca Mar 17 '13 at 19:45
  • On one extreme, for really simple programs or you just directly work with argv[] array. Another situation, for total flexibility in your arguments you can work with argv array directly (you can do prog -1 firstinput -2 second input -obj {constructor arguments ..}). Otherwise use boost, tclap, or many others. – Kemin Zhou Nov 01 '16 at 16:53
  • `boost::program_options` is hopelessly overengineered, difficult to use, and underdocumented. One of the few Boost libraries that would greatly benefit from a complete redesign and rewrite. Don't use it if you can avoid it. – András Aszódi Mar 24 '20 at 14:18
37

You can use GNU GetOpt (LGPL) or one of the various C++ ports, such as getoptpp (GPL).

A simple example using GetOpt of what you want (prog [-ab] input) is the following:

// C Libraries:
#include <string>
#include <iostream>
#include <unistd.h>

// Namespaces:
using namespace std;

int main(int argc, char** argv) {
    int opt;
    string input = "";
    bool flagA = false;
    bool flagB = false;

    // Retrieve the (non-option) argument:
    if ( (argc <= 1) || (argv[argc-1] == NULL) || (argv[argc-1][0] == '-') ) {  // there is NO input...
        cerr << "No argument provided!" << endl;
        //return 1;
    }
    else {  // there is an input...
        input = argv[argc-1];
    }

    // Debug:
    cout << "input = " << input << endl;

    // Shut GetOpt error messages down (return '?'): 
    opterr = 0;

    // Retrieve the options:
    while ( (opt = getopt(argc, argv, "ab")) != -1 ) {  // for each option...
        switch ( opt ) {
            case 'a':
                    flagA = true;
                break;
            case 'b':
                    flagB = true;
                break;
            case '?':  // unknown option...
                    cerr << "Unknown option: '" << char(optopt) << "'!" << endl;
                break;
        }
    }

    // Debug:
    cout << "flagA = " << flagA << endl;
    cout << "flagB = " << flagB << endl;

    return 0;
}
Paolo Rovelli
  • 9,396
  • 2
  • 58
  • 37
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
27

GNU GetOpt.

A simple example using GetOpt:

// C/C++ Libraries:
#include <string>
#include <iostream>
#include <unistd.h>

// Namespaces:
using namespace std;

int main(int argc, char** argv) {
    int opt;
    bool flagA = false;
    bool flagB = false;

    // Shut GetOpt error messages down (return '?'): 
    opterr = 0;

    // Retrieve the options:
    while ( (opt = getopt(argc, argv, "ab")) != -1 ) {  // for each option...
        switch ( opt ) {
            case 'a':
                    flagA = true;
                break;
            case 'b':
                    flagB = true;
                break;
            case '?':  // unknown option...
                    cerr << "Unknown option: '" << char(optopt) << "'!" << endl;
                break;
        }
    }

    // Debug:
    cout << "flagA = " << flagA << endl;
    cout << "flagB = " << flagB << endl;

    return 0;
}

You can also use optarg if you have options that accept arguments.

Paolo Rovelli
  • 9,396
  • 2
  • 58
  • 37
Marcin Gil
  • 68,043
  • 8
  • 59
  • 60
  • 15
    Ughh. I understand the use of this library in C code, but IMO, this is way too low level to be acceptable in any C++ application I've ever written. Find a better library if you don't need pure C. – Thomas Eding Feb 09 '15 at 22:52
  • 1
    There is also a GNU example of getopt with a value e.g. myexe -c myvalue You can try to search for "Example of Parsing Arguments with getopt" – JayS May 07 '17 at 17:16
  • here's an [example](https://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html#Example-of-Getopt) example provided by the GNU itself ^^ – finnmglas Jul 23 '20 at 19:24
23

Yet another alternative is The Lean Mean C++ Option Parser:

http://optionparser.sourceforge.net

It is a header-only library (just a single header file, in fact) and unlike all the other suggestions it is also freestanding, i.e. it has no dependencies whatsoever. In particular there's no dependency on the STL. It does not even use exceptions or anything else that requires library support. This means it can be linked with plain C or other languages without introducing "foreign" libraries.

Like boost::program_options its API offers convenient direct access to options, i.e. you can write code like this

if (options[HELP]) ... ;

and

int verbosity = options[VERBOSE].count();

Unlike boost::program_options however this is simply using an array indexed with a (user-provided) enum. This offers the convenience of an associative container without the weight.

It's well documented and has a company-friendly license (MIT).

TLMC++OP includes a nice formatter for usage messages that can do line-wrapping and column alignment which is useful if you're localizing your program, because it ensures that the output will look good even in languages that have longer messages. It also saves you the nuisance of manually formatting your usage for 80 columns.

MSB
  • 306
  • 2
  • 4
21
for (int i = 1; i < argc; i++) {

    if (strcmp(argv[i],"-i")==0) {
        filename = argv[i+1];
        printf("filename: %s",filename);
    } else if (strcmp(argv[i],"-c")==0) {
        convergence = atoi(argv[i + 1]);
        printf("\nconvergence: %d",convergence);
    } else if (strcmp(argv[i],"-a")==0) {
        accuracy = atoi(argv[i + 1]);
        printf("\naccuracy:%d",accuracy);
    } else if (strcmp(argv[i],"-t")==0) {
        targetBitRate = atof(argv[i + 1]);
        printf("\ntargetBitRate:%f",targetBitRate);
    } else if (strcmp(argv[i],"-f")==0) {
        frameRate = atoi(argv[i + 1]);
        printf("\nframeRate:%d",frameRate);
    }

}
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Oliver Nina
  • 655
  • 6
  • 4
  • 4
    -1: this fetches array elements without bound checks – Otto Allmendinger Dec 26 '11 at 13:42
  • 2
    -1: because of no bounds checking – Sebastian Mach Apr 10 '12 at 11:18
  • 9
    @RobertMunafo: The references to `argv[i+1]` can easily go outside the bounds of the `argv` array. Think about running the program with `"-i"` as the *last* argument. – Keith Thompson Jul 02 '12 at 22:32
  • 1
    @RobertMunafo: correct, `argv[argc]` is required to be `0`. Assigning that to `filename` and passing it to `printf()` will crash at runtime, though. And even if we don't fall off the end, the code doesn't sufficiently advance `i` after it parses an argument. It will try to parse the argument value as an option name next time around the loop. – Marc Mutz - mmutz Jul 02 '12 at 22:38
  • 2
    -1 for being a "roll-your-own" answer, whereas the question asks specifically for a "library in STL" that will do the task. Also, as noted by Keith Thompson and mmutz, there are some bugs in bounds checking. – Robert Munafo Jul 03 '12 at 00:11
  • 31
    I found the comments really harsh. I think it's fair to show also an example of how this can be done without using a library. This answer is complementary +1, sorry. – user18490 Jun 29 '14 at 14:04
  • 1
    @Robert Munafo the asks for a way to parse the command line and additionally wether there is an STL-lib. – Beginner Jul 11 '17 at 07:59
  • Good solution! - Why add a library for something that can be done in a few lines of rather simple code. Especially if your program doesn't use any other libraries, and considering that C++ doesn't have a standard package manager yet. With bounds checking, as suggested above: e.g. `if (strcmp(argv[i], "-i") == 0 && argv[i+1]) { filename = argv[++i]; ... }` – mh8020 Jan 10 '21 at 21:47
20

TCLAP is a really nice lightweight design and easy to use: http://tclap.sourceforge.net/

Sean
  • 9,888
  • 4
  • 40
  • 43
cheshirekow
  • 4,797
  • 6
  • 43
  • 47
  • 4
    I've used getopt, google's gflags, program_options from Boost and tclap is *fantastic*. I can't say enough good things about tclap, especially considering the available alternatives. The extent of the gripes that I have is that it's help formatting is "different" than what my eye is used to. – Sean Jan 21 '13 at 06:29
18

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.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • +1, didn't know about tclap and it manages to be lightweight and yet feels complete, I'm definitely going to delve deeper. – Matthieu M. Sep 30 '10 at 06:33
18

If you just want to process command line options yourself, the easiest way is to put:

vector<string> args(argv + 1, argv + argc);

at the top of your main(). This copies all command-line arguments into a vector of std::strings. Then you can use == to compare strings easily, instead of endless strcmp() calls. For example:

int main(int argc, char **argv) {
    vector<string> args(argv + 1, argv + argc);
    string infname, outfname;

    // Loop over command-line args
    // (Actually I usually use an ordinary integer loop variable and compare
    // args[i] instead of *i -- don't tell anyone! ;)
    for (auto i = args.begin(); i != args.end(); ++i) {
        if (*i == "-h" || *i == "--help") {
            cout << "Syntax: foomatic -i <infile> -o <outfile>" << endl;
            return 0;
        } else if (*i == "-i") {
            infname = *++i;
        } else if (*i == "-o") {
            outfname = *++i;
        }
    }
}

[EDIT: I realised I was copying argv[0], the name of the program, into args -- fixed.]

xorover
  • 94
  • 9
j_random_hacker
  • 50,331
  • 10
  • 105
  • 169
  • Added a simple example. Really all the vector buys you is simple comparisons with == and convenient by-value copying. – j_random_hacker Jan 15 '09 at 09:48
  • This doesn't help to do anything that can't be already done with argc/argv. – brofield Apr 21 '09 at 08:48
  • @brofield: Sure, it doesn't change the world. But I find the == and value semantics simplify things enough that I keep using it. – j_random_hacker Apr 21 '09 at 14:00
  • @j_random_hacker - what if I want to remember the order of the arguments? For instance, if the user typed `mycommand.exe -h file.csv`, I want to tell them that they are not using the utility correctly and why (should not supply a file name if they are just using the version). This example is rather simple, but I can think of more convoluted flags. The end result would be: sometimes the order does matter, and sometimes it does not. So ... how should I proceed then? Let me know if you have a question about my question. – Hamish Grubijan Jul 15 '10 at 21:53
  • @Hamish: I'm a bit confused -- loading the strings into a vector doesn't "lose" their order. You can still access the ith argument with `args[i]` (in fact I often do this myself, as the comment in my code snippet says). The iterator style is just a bit more convenient if you only need to deal with one at a time. Does that answer your question? – j_random_hacker Jul 16 '10 at 01:36
18

I find it easier to use ezOptionParser. It's also a single header file, does not depend on anything but STL, works for Windows and Linux (very likely other platforms too), has no learning curve thanks to the examples, has features other libraries don't (like file import/export with comments, arbitrary option names with delimiters, auto usage formatting, etc), and is LGPL licensed.

remikz
  • 438
  • 6
  • 4
  • 1
    Starting with version 0.1.3, the license is now MIT. I'm trying this out on a new project instead of TCLAP and so far it looks very promising. The file config option is quite nice. – Sean Apr 09 '13 at 21:20
  • 6
    I just tried out exOptionParser, but it has so many problems. First of all, I get 58 warnings about unsigned int to int conversion. It also tries to increment list iterators (which can't be used like that) and crashes. Its interface is so terrible as well. It uses references all over the place instead of just returning the data you want. It looks like a C library even though it's built on top of the C++ STL. – Andrew Larsson Feb 05 '14 at 07:54
  • Note; detection of unknown arguments don't work. Also, the header gives compile errors if not placed before other headers. I'll look for another parser.. – Totte Karlsson Mar 18 '19 at 23:55
16

And there's a Google library available.

Really, command-line parsing is "solved." Just pick one.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
  • (Thank you @QPaysTaxes for noticing the link was broken; I don't know why your edit was rejected, but you were definitely correct). – Max Lybbert Jan 14 '16 at 16:28
  • 5
    I can't think of a less helpful reply to a question. 'It's solved. Pick one.' Sorry, but "duh." With my about um 15 minutes of thinking about this problem, I have come up with about 30 different scenarios about how one could approach it. I suspect the 'correct' response is more akin to explaining how a particular set of concerns would lead to a particular set of code implementations. But, hey, thanks for calling. –  Mar 25 '20 at 02:35
15

With C++, the answer is usually in Boost...

Boost.Program Options

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
11

Try Boost::Program Options. It allows you to read and parse command lines as well as config files.

thekidder
  • 3,486
  • 1
  • 33
  • 36
  • Does it allow you to show the current and default values? A few years ago I had to implement a wrapper around it to show the effective options (based on parsed configs/args, and not just showing the strings supplied). – Jonathan Graehl Sep 17 '09 at 03:24
9

I think that GNU GetOpt is not too immediate to use.

Qt and Boost could be a solution, but you need to download and compile a lot of code.

So I implemented a parser by myself that produces a std::map<std::string, std::string> of parameters.

For example, calling:

 ./myProgram -v -p 1234

map will be:

 ["-v"][""]
 ["-p"]["1234"]

Usage is:

int main(int argc, char *argv[]) {
    MainOptions mo(argc, argv);
    MainOptions::Option* opt = mo.getParamFromKey("-p");
    const string type = opt ? (*opt).second : "";
    cout << type << endl; /* Prints 1234 */
    /* Your check code */
}

MainOptions.h

#ifndef MAINOPTIONS_H_
#define MAINOPTIONS_H_

#include <map>
#include <string>

class MainOptions {
public:
    typedef std::pair<std::string, std::string> Option;
    MainOptions(int argc, char *argv[]);
    virtual ~MainOptions();
    std::string getAppName() const;
    bool hasKey(const std::string&) const;
    Option* getParamFromKey(const std::string&) const;
    void printOptions() const;
private:
    typedef std::map<std::string, std::string> Options;
    void parse();
    const char* const *begin() const;
    const char* const *end() const;
    const char* const *last() const;
    Options options_;
    int argc_;
    char** argv_;
    std::string appName_;
};

MainOptions.cpp

#include "MainOptions.h"

#include <iostream>

using namespace std;

MainOptions::MainOptions(int argc, char* argv[]) :
        argc_(argc),
        argv_(argv) {
    appName_ = argv_[0];
    this->parse();
}

MainOptions::~MainOptions() {
}

std::string MainOptions::getAppName() const {
    return appName_;
}

void MainOptions::parse() {
    typedef pair<string, string> Option;
    Option* option = new pair<string, string>();
    for (const char* const * i = this->begin() + 1; i != this->end(); i++) {
        const string p = *i;
        if (option->first == "" && p[0] == '-') {
            option->first = p;
            if (i == this->last()) {
                options_.insert(Option(option->first, option->second));
            }
            continue;
        } else if (option->first != "" && p[0] == '-') {
            option->second = "null"; /* or leave empty? */
            options_.insert(Option(option->first, option->second));
            option->first = p;
            option->second = "";
            if (i == this->last()) {
                options_.insert(Option(option->first, option->second));
            }
            continue;
        } else if (option->first != "") {
            option->second = p;
            options_.insert(Option(option->first, option->second));
            option->first = "";
            option->second = "";
            continue;
        }
    }
}

void MainOptions::printOptions() const {
    std::map<std::string, std::string>::const_iterator m = options_.begin();
    int i = 0;
    if (options_.empty()) {
        cout << "No parameters\n";
    }
    for (; m != options_.end(); m++, ++i) {
        cout << "Parameter [" << i << "] [" << (*m).first << " " << (*m).second
                << "]\n";
    }
}

const char* const *MainOptions::begin() const {
    return argv_;
}

const char* const *MainOptions::end() const {
    return argv_ + argc_;
}

const char* const *MainOptions::last() const {
    return argv_ + argc_ - 1;
}

bool MainOptions::hasKey(const std::string& key) const {
    return options_.find(key) != options_.end();
}

MainOptions::Option* MainOptions::getParamFromKey(
        const std::string& key) const {
    const Options::const_iterator i = options_.find(key);
    MainOptions::Option* o = 0;
    if (i != options_.end()) {
        o = new MainOptions::Option((*i).first, (*i).second);
    }
    return o;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
8

There are these tools in the GNU C Library, which includes GetOpt.

If you are using Qt and like the GetOpt interface, froglogic has published a nice interface here.

Dusty Campbell
  • 3,146
  • 31
  • 34
7

Tooting my own horn if I may, I'd also like to suggest taking a look at an option parsing library that I've written: dropt.

  • It's a C library (with a C++ wrapper if desired).
  • It's lightweight.
  • It's extensible (custom argument types can be easily added and have equal footing with built-in argument types).
  • It should be very portable (it's written in standard C) with no dependencies (other than the C standard library).
  • It has a very unrestrictive license (zlib/libpng).

One feature that it offers that many others don't is the ability to override earlier options. For example, if you have a shell alias:

alias bar="foo --flag1 --flag2 --flag3"

and you want to use bar but with--flag1 disabled, it allows you to do:

bar --flag1=0
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • 1
    This looks pretty neat. Glad I scrolled down; there's just nothing very good for plain C, save this! – Asherah Feb 21 '14 at 10:28
  • Sounds great, but it seems a bit too big. Also, I'm just looking for a way to parse out scanf format specifiers from an argument, I've already written my own (albeit more basic) parser. – MarcusJ Aug 09 '16 at 17:43
  • 1
    @MarcusJ It seems a little weird that you say that this is too big (it's much smaller than most other command-line option parser) yet that you want it to parse printf/scanf format specifiers (which isn't something that command-line option parsers typically do)... – jamesdlin Aug 09 '16 at 19:35
  • Yeah, I know I've got some specific requirements for this, I'm just gonna go ahead and rewrite my option parser, but I did look at your code and the idea of passing in a struct to contain the various options is really interesting (so far mine is just hard coded). It's not too big on it's own, it's just that I've got a single .c/.h project, and your code would double the amount of code I've already got, so it's too big for my specific project. – MarcusJ Aug 10 '16 at 03:28
6

I like C's getopt(), but then I'm old. :-)

John Deters
  • 4,295
  • 25
  • 41
6

Google's gflags

Craig
  • 4,750
  • 22
  • 21
  • Looks nice for simple use. I've always liked "options as global variables defined anywhere you please" for small tools. Primitives + string only which can be a big minus. – Jonathan Graehl Sep 17 '09 at 03:38
5

Qt 5.2 comes with a command line parser API.

Small example:

#include <QCoreApplication>
#include <QCommandLineParser>
#include <QDebug>

int main(int argc, char **argv)
{
  QCoreApplication app(argc, argv);
  app.setApplicationName("ToolX");
  app.setApplicationVersion("1.2");

  QCommandLineParser parser;
  parser.setApplicationDescription("Tool for doing X.");
  parser.addHelpOption();
  parser.addVersionOption();
  parser.addPositionalArgument("infile",
      QCoreApplication::translate("main", "Input file."));

  QCommandLineOption verbose_opt("+",
      QCoreApplication::translate("main", "be verbose"));
  parser.addOption(verbose_opt);

  QCommandLineOption out_opt(QStringList() << "o" << "output",
      QCoreApplication::translate("main", "Output file."),
      QCoreApplication::translate("main", "filename"), // value name
      QCoreApplication::translate("main", "out")   // default value
      );
  parser.addOption(out_opt);

  // exits on error
  parser.process(app);

  const QStringList args = parser.positionalArguments();

  qDebug() << "Input files: " << args
    << ", verbose: " << parser.isSet(verbose_opt)
    << ", output: " << parser.value(out_opt)
    << '\n';
  return 0;
}

Example output

The automatically generated help screen:

$ ./qtopt -h
Usage: ./qtopt [options] infile
Tool for doing X.

Options:
  -h, --help               Displays this help.
  -v, --version            Displays version information.
  -+                       be verbose
  -o, --output   Output file.

Arguments:
  infile                   Input file.

Automatically generated version output:

$ ./qtopt -v
ToolX 1.2

Some real calls:

$ ./qtopt b1 -+ -o tmp blah.foo
Input files:  ("b1", "blah.foo") , verbose:  true , output:  "tmp"
$ ./qtopt          
Input files:  () , verbose:  false , output:  "out"

A parse error:

$ ./qtopt --hlp
Unknown option 'hlp'.
$ echo $?
1

Conclusion

If your program already use the Qt (>= 5.2) libraries, its command line parsing API is convenient enough to get the job done.

Be aware that builtin Qt options are consumed by QApplication before the option parser runs.

maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
5

I'd suggest using a library. There's the classic and venerable getopt and I'm sure others.

Aaron Maenpaa
  • 119,832
  • 11
  • 95
  • 108
5

AnyOption is a C++ class for easy parsing of complex commandline options. It also parses options from a rsourcefile in option value pair format.

AnyOption implements the traditional POSIX style character options ( -n ) as well as the newer GNU style long options ( --name ). Or you can use a simpler long option version ( -name ) by asking to ignore the POSIX style options.

Jay
  • 13,803
  • 4
  • 42
  • 69
5

There are a number of good libraries available.

Boost Program Options is a fairly heavyweight solution, both because adding it to your project requires you to build boost, and the syntax is somewhat confusing (in my opinion). However, it can do pretty much everything including having the command line options override those set in configuration files.

SimpleOpt is a fairly comprehensive but simple command line processor. It is a single file and has a simple structure, but only handles the parsing of the command line into options, you have to do all of the type and range checking. It is good for both Windows and Unix and comes with a version of glob for Windows too.

getopt is available on Windows. It is the same as on Unix machines, but it is often a GPL library.

brofield
  • 2,226
  • 1
  • 22
  • 25
4

A command is basically a string. In general it can be split into two parts - the command's name and the command's arguments.

Example:

ls

is used for listing the contents of a directory:

user@computer:~$ ls
Documents Pictures Videos ...

The ls above is executed inside home folder of a user. Here the argument which folder to list is implicitly added to the command. We can explicitly pass some arguments:

user@computer:~$ ls Picture
image1.jpg image2.jpg ...

Here I have explicitly told ls which folder's contents I'd like to see. We can use another argument for example l for listing the details of each file and folder such as access permissions, size etc.:

user@computer:~$ ls Pictures
-rw-r--r-- 1 user user   215867 Oct 12  2014 image1.jpg
-rw-r--r-- 1 user user   268800 Jul 31  2014 image2.jpg
...

Oh, the size looks really weird (215867, 268800). Let's add the h flag for human-friendly output:

user@computer:~$ ls -l -h Pictures
-rw-r--r-- 1 user user  211K Oct 12  2014 image1.jpg
-rw-r--r-- 1 user user  263K Jul 31  2014 image2.jpg
...

Some commands allow their arguments to be combined (in the above case we might as well write ls -lh and we'll get the same output), using short (a single letter usually but sometimes more; abbreviation) or long names (in case of ls we have the -a or --all for listing all files including hidden ones with --all being the long name for -a) etc. There are commands where the order of the arguments is very important but there are also others where the order of the arguments is not important at all.

For example it doesn't matter if I use ls -lh or ls -hl however in the case of mv (moving/renaming files) you have less flexibility for your last 2 arguments that is mv [OPTIONS] SOURCE DESTINATION.

In order to get a grip of commands and their arguments you can use man (example: man ls) or info (example: info ls).

In many languages including C/C++ you have a way of parsing command line arguments that the user has attached to the call of the executable (the command). There are also numerous libraries available for this task since in its core it's actually not that easy to do it properly and at the same time offer a large amount of arguments and their varieties:

  • getopt
  • argp_parse
  • gflags
  • ...

Every C/C++ application has the so called entry point, which is basically where your code starts - the main function:

int main (int argc, char *argv[]) { // When you launch your application the first line of code that is ran is this one - entry point
    // Some code here
    return 0; // Exit code of the application - exit point
}

No matter if you use a library (like one of the above I've mentioned; but this is clearly not allowed in your case ;)) or do it on your own your main function has the two arguments:

  • argc - represents the number of arguments
  • argv - a pointer to an array of strings (you can also see char** argv which is basically the same but more difficult to use).

NOTE: main actually also has a third argument char *envp[] which allows passing environment variables to your command but this is a more advanced thing and I really don't think that it's required in your case.

The processing of command line arguments consists of two parts:

  1. Tokenizing - this is the part where each argument gets a meaning. Its the process of breaking your arguments list into meaningful elements (tokens). In the case of ls -l the l is not only a valid character but also a token in itself since it represents a complete, valid argument.

Here is an example how to output the number of arguments and the (unchecked for validity) characters that may or may not actually be arguments:

#include <iostream>
using std::cout;
using std::endl;

int main (int argc, char *argv[]) {
    cout << "Arguments' count=%d" << argc << endl;

    // First argument is ALWAYS the command itself
    cout << "Command: " << argv[0] << endl;

    // For additional arguments we start from argv[1] and continue (if any)
    for (int i = 1; i < argc; i++) {
        cout << "arg[" << i << "]: " << argv[i] << endl;
    }

    cout << endl;
    return 0;
}
  1. Parsing - after acquiring the tokens (arguments and their values) you need to check if your command supports these. For example:

    user@computer:~$ ls -y
    

    will return

    ls: invalid option -- 'y'
    Try 'ls --help' for more information.
    

    This is because the parsing has failed. Why? Because y (and -y respectively; note that -, --, : etc. is not required and its up to the parsing of the arguments whether you want that stuff there or not; in Unix/Linux systems this is a sort of a convention but you are not bind to it) is an unknown argument for the ls command.

For each argument (if successfully recognized as such) you trigger some sort of change in your application. You can use an if-else for example to check if a certain argument is valid and what it does followed by changing whatever you want that argument to change in the execution of the rest of your code. You can go the old C-style or C++-style:

* `if (strcmp(argv[1], "x") == 0) { ... }` - compare the pointer value
* `if (std::string(argv[1]) == "x") { ... }` - convert to string and then compare

I actually like (when not using a library) to convert argv to an std::vector of strings like this:

std::vector<std::string> args(argv, argv+argc);
for (size_t i = 1; i < args.size(); ++i) {
    if (args[i] == "x") {
        // Handle x
    }
    else if (args[i] == "y") {
        // Handle y
    }
    // ...
}

The std::vector<std::string> args(argv, argv+argc); part is just an easier C++-ish way to handle the array of strings since char * is a C-style string (with char *argv[] being an array of such strings) which can easily be converted to a C++ string that is std::string. Then we can add all converted strings to a vector by giving the starting address of argv and then also pointing to its last address namely argv + argc (we add argc number of string to the base address of argv which is basically pointing at the last address of our array).

Inside the for loop above you can see that I check (using simple if-else) if a certain argument is available and if yes then handle it accordingly. A word of caution: by using such a loop the order of the arguments doesn't matter. As I've mentioned at the beginning some commands actually have a strict order for some or all of their arguments. You can handle this in a different way by manually calling the content of each args (or argv if you use the initial char* argv[] and not the vector solution):

// No for loop!
if (args[1] == "x") {
    // Handle x
}
else if (args[2] == "y") {
    // Handle y
}
// ...

This makes sure that at position 1 only the x will be expected etc. The problem with this is that you can shoot yourself in the leg by going out of bounds with the indexing so you have to make sure that your index stays within the range set by argc:

if (argc > 1 && argc <= 3) {
    if (args[1] == "x") {
        // Handle x
    }
    else if (args[2] == "y") {
        // Handle y
    }
}

The example above makes sure you have content at index 1 and 2 but not beyond.

Last but not least the handling of each argument is a thing that is totally up to you. You can use boolean flags that are set when a certain argument is detected (example: if (args[i] == "x") { xFound = true; } and later on in your code do something based on the bool xFound and its value), numerical types if the argument is a number OR consists of number along with the argument's name (example: mycommand -x=4 has an argument -x=4 which you can additionally parse as x and 4 the last being the value of x) etc. Based on the task at hand you can go crazy and add an insane amount of complexity to your command line arguments.

Hope this helps. Let me know if something is unclear or you need more examples.

rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
3

argstream is quite similar to boost.program_option: it permits to bind variables to options, etc. However it does not handle options stored in a configuration file.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
Luc Hermitte
  • 31,979
  • 7
  • 69
  • 83
3

Try CLPP library. It's simple and flexible library for command line parameters parsing. Header-only and cross-platform. Uses ISO C++ and Boost C++ libraries only. IMHO it is easier than Boost.Program_options.

Library: http://sourceforge.net/projects/clp-parser/

26 October 2010 - new release 2.0rc. Many bugs fixed, full refactoring of the source code, documentation, examples and comments have been corrected.

Denis Shevchenko
  • 1,342
  • 2
  • 10
  • 23
3

You could use an already created library for this

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

David
  • 3,324
  • 2
  • 27
  • 31
3

I'd recommend boost::program_options if you can use the Boost lib.

There's nothing specific in STL nor in the regular C++/C runtime libs.

Macke
  • 24,812
  • 7
  • 82
  • 118
2

Boost program_options.

bobbymcr
  • 23,769
  • 3
  • 56
  • 67
2

Try CLPP library. It's simple and flexible library for command line parameters parsing. Header-only and cross-platform. Uses ISO C++ and Boost C++ libraries only. IMHO it is easier than Boost.Program_options.

Library: http://sourceforge.net/projects/clp-parser

26 October 2010 - new release 2.0rc. Many bugs fixed, full refactoring of the source code, documentation, examples and comments have been corrected.

Denis Shevchenko
  • 1,342
  • 2
  • 10
  • 23
2

This is my favourite way of doing the command line, especially, but definitely not only when efficiency is an issue. It might seem overkill, but I think there are few disadvantages to this overkill.

Use gperf for efficient C/C++ command line processing

Disadvantages:

  • You have to run a separate tool first to generate the code for a hash table in C/C++
  • No support for specific command line interfaces. For example the posix shorthand system "-xyz" declaring multiple options with one dash would be hard to implement.

Advantages:

  • Your command line options are stored separately from your C++ code (in a separate configuration file, which doesn't need to be read at runtime, only at compile time).
  • All you have in your code is exactly one switch (switching on enum values) to figure out which option you have
  • Efficiency is O(n) where n is the number of options on the command line and the number of possible options is irrelevant. The slowest part is possibly the implementation of the switch (sometimes compilers tend to implement them as if else blocks, reducing their efficiency, albeit this is unlikely if you choose contiguous values, see: this article on switch efficiency )
  • The memory allocated to store the keywords is precisely large enough for the keyword set and no larger.
  • Also works in C

Using an IDE like eclipse you can probably automate the process of running gperf, so the only thing you would have to do is add an option to the config file and to your switch statement and press build...

I used a batch file to run gperf and do some cleanup and add include guards with sed (on the gperf generated .hpp file)...

So, extremely concise and clean code within your software and one auto-generated hash table file that you don't really need to change manually. I doubt if boost::program_options actually would beat that even without efficiency as a priority.

2

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

pm100
  • 48,078
  • 23
  • 82
  • 145
  • Not really as the question was about C++ and Getopt is just plain C. There used to be a C++ variant of it but for some reason it was withdrawn. – Dirk Eddelbuettel Sep 30 '10 at 00:52
  • 1
    it works fine in c++ tho; its what we use in all our c++ code. – pm100 Sep 30 '10 at 01:04
  • Well yes but you can do *much* better with e.g. TCLAP. I add or remove one line with new option definition and I do not need to edit code in other place --> not so true with old school getopt. – Dirk Eddelbuettel Sep 30 '10 at 01:51
2

I have used GetPot for some projects: http://getpot.sourceforge.net/

Main feature: everything is in a single header file, no build hassles. Just save it somewhere on your machine and "#include" it in your file holding main()

Hasn't be updated recently, but it is nicely documented, and works well.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
kebs
  • 6,387
  • 4
  • 41
  • 70
  • It returns false from functions, that is supposed to return char*, how is that possible? (the function is inline const char* GetPot::__match_starting_string(const char* StartString)) – Asalle May 15 '17 at 06:22
  • @Asalle No idea, but this seems like an "inside" function, not part of the API. In case you have a doubt, better ask the author. – kebs May 16 '17 at 07:14
1

You could try my little options header (166 loc so easily hackable) options.hpp. It is a single header implementation and should do what you ask. It also prints you the help page automatically.

burner
  • 323
  • 2
  • 10
1

Following from my comment and from rbaleksandar's answer, the arguments passed to any program in C are string values. You are provided the argument count (argc) which gives you the argument indexes zero-based beginning with the name of the program currently being run (which is always argv[0]). That leaves all arguments between 1 - argc as the user supplied arguments for your program. Each will be a string that is contained in the argument vector (which is a pointer to an array of strings you will seen written as char *argv[], or equivalently as a function parameter char **argv) Each of the strings argv[1] to argv[argc-1] are available to you, you simply need to test which argument is which.

That will allow you to separate, and make them available as the command (cmd), the options (opt) and finally the argument (arg) to your cmd.

Now it is worth noting, that the rules of your shell (bash, etc..) apply to the arguments passed to your program, word-splitting, pathname and variable expansion apply before your code gets the arguments. So you must consider whether single or more commongly double-quoting will be required around any of your arguments to prevent the normal shell splitting that would otherwise apply (e.g. ls -al my file.txt would results in 4 user-supplied arguments to your code, while ls -al "my file.txt" or ls -al my\ file.txt which would result in the 3 your were expecting.

Putting all that together, your short bit of parsing could be done something like what follows. (you are also free to do it however you like, using a switch instead of nested ifs, etc...)

#include <stdio.h>

int main (int argc, char **argv) {

    char *cmd = NULL,   /* here, since you are using the arguments  */
         *opt = NULL,   /* themselves, you can simply use a pointer */
         *arg = NULL;   /* or the argument itself without a copy    */

    /* looping using the acutal argument index & vector */
    for (int i = 1; i < argc; i++) {
        if (*argv[i] != '-') {      /* checking if the 1st char is - */
            if (!cmd)               /* cmd is currently NULL, and    */
                cmd = argv[i];      /* no '-' it's going to be cmd   */
            else                    /* otherwise, cmd has value, so  */
                arg = argv[i];       /* the value will be opt        */
        }
        else                /* here the value has a leading '-', so  */
            opt = argv[i];  /* it will be the option */
    }

    printf ("\n cmd : %s\n opt : %s\n arg : %s\n\n",
            cmd, opt, arg);

    return 0;
}

Example Use/Output

If you run the code, you will find it provides separation for the arguments and provides separate pointers to facilitate their use:

$ ./bin/parse_cmd ls -la ./cs3000

 cmd : ls
 opt : -la
 arg : ./cs3000

(it is important to note, that if you were tasked with building a command string where you would need to copy multiple value to say opt or arg, then you could no longer simply use a pointer and would need to create storage, either though a simple declaring of arrays instead of pointer to begin with, or you could dynamically allocate storage as required with, e.g. malloc, calloc and/or realloc. Then you would have storage available to copy and concatenate values within.)

If this was your challenge, then between all the answer here, you should have a handle of how to approach your problem. If you must go further and actually have your program execute the cmd with the opt and the arg, then you will want to look at fork to spawn a semi-separate process within which you run would execute your cmd opt and arg with something similar to execv or execvp. Good luck, and post a comment if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1

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.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
0

Your C/C++ program always has a main function. It looks like that:

    int main(int argc, char**argv) {
        ...
    }

Here argc is a number of command line arguments, that have been passed to your program, and argv is an array of strings with these arguments. So command line is separated in-to arguments by the caller process (this is not a single line, like in windows).

Now you need to sort them out:

  • Command name is always the first argument (index 0).
  • Options are just special arguments that specify how a program should work. By convention they start from - sign. Usually - for one letter options and -- for anything longer. So in your task "options" are all arguments, that start from - and are not the 0th.
  • Arguments. Just all other arguments which are not program name or options.
Alexey Guseynov
  • 5,116
  • 1
  • 19
  • 29
0

Try CLPP library. It's simple and flexible library for command line parameters parsing. Header-only and cross-platform. Uses ISO C++ and Boost C++ libraries only. IMHO it is easier than Boost.Program_options.

Library: http://sourceforge.net/projects/clp-parser

26 October 2010 - new release 2.0rc. Many bugs fixed, full refactoring of the source code, documentation, examples and comments have been corrected.

Denis Shevchenko
  • 1,342
  • 2
  • 10
  • 23
0

If you don't want to use boost, I'd recommend this little helper class.

Stefan
  • 43,293
  • 10
  • 75
  • 117
0

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;
...
Achim S.
  • 1
  • 1
-1

A simple solution is to put argv into a std::map for easy lookups:

map<string, string> argvToMap(int argc, char * argv[])
{
    map<string, string> args;

    for(int i=1; i<argc; i++) {
        if (argv[i][0] == '-') {
            const string key = argv[i];
            string value = "";
            if (i+1 < argc && argv[i+1][0] != '-') {
                value = string(argv[i+1]);
                i++;
            }

            args[key] = value;
        }
    }

    return args;
}

Example usage:

#include <map>
#include <string>
#include <iostream>

using namespace std;

map<string, string> argvToMap(int argc, char * argv[])
{
    map<string, string> args;

    for(int i=1; i<argc; i++) {
        if (argv[i][0] == '-') {
            const string key = argv[i];
            string value = "";
            if (i+1 < argc && argv[i+1][0] != '-') {
                value = string(argv[i+1]);
                i++;
            }

            args[key] = value;
        }
    }

    return args;
}

void printUsage()
{
    cout << "simple_args: A sample program for simple arg parsing\n"
            "\n"
            "Example usage:\n"
            "    ./simple_args --print-all --option 1 --flag 2\n";
}

int main(int argc, char * argv[])
{
    auto args = argvToMap(argc, argv);

    if (args.count("-h") || args.count("--help")) {
        printUsage();
    }
    else if (args.count("--print-all")) {
        for (auto const & pair: args)
            cout << "{" << pair.first << ": " << pair.second << "}\n";
    }

    return 0;
}

Output:

$ ./simple_args --print-all --option 1 --flag "hello world"
{--flag: hello world}
{--option: 1}
{--print-all: }

There are definitely significant limitations to this approach, but I found it struck a good balance of simplicity and utility.

Gillespie
  • 5,780
  • 3
  • 32
  • 54