Parsing Command Line Arguments in C++? [closed]
Asked Answered
S

42

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:

Suborder answered 14/5, 2009 at 20:46 Comment(6)
I think the Nunit source code (C#) has a good example of a command line handling class....Juggernaut
The easiest would be to use one of the argument parsing libraries: getopt or argparse.Anny
If you can't use libraries (e.g. boost), at least use std::vector<std::string> args(argv, argv+argc); so you can parse a vector of strings instead of an array of char-arrays.Radiography
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.]Melodics
Recently wrote this for modern c++: github.com/juzzlin/ArgenginePaulitapaulk
chechout github.com/p-ranav/argparseTrussell
V
90

Boost.Program_options should do the trick

Vermilion answered 14/5, 2009 at 20:49 Comment(7)
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.Hypersthene
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.Ribbentrop
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.Boyce
Furthermore, boost::program_options is not a header only library. You'll have to build boost. This is very troublesome.Mcbee
boost seems like a total overkill for this taskTimberlake
This answer is crying out for a simple example IMHO.Bimestrial
reddit.com/r/cpp/comments/4zhm2n/…Pontone
C
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;
}
Chokecherry answered 15/5, 2009 at 14:9 Comment(11)
I Rick you are correct about the #include thanks for updating my answerChokecherry
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 ?Gaitskell
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).Chokecherry
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.Carcanet
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.Chokecherry
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 #1340101. Other than that, a nice and simple solution, I'll use that, thanks.Hambletonian
An important note: This class does not compile as is with Visual Studio compiler (I'm using 2010 version). You must explicitly include <string> header to your program (iostream and vector are not sufficient). Got this solution from : #9080528Ashling
1. @Chokecherry 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.Eerie
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.Chokecherry
@Chokecherry I am getting an error: no instance of function template "std::find" matches the argument list argument types are: (__gnu_cxx::__normal_iterator<const std::__cxx11::string *, std::vector<std::__cxx11::string, std::allocator<std::__cxx11::string>>>, __gnu_cxx::__normal_iterator<const std::__cxx11::string *, std::vector<std::__cxx11::string, std::allocator<std::__cxx11::string>>>, const std::__cxx11::string)Teran
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.Chokecherry
V
90

Boost.Program_options should do the trick

Vermilion answered 14/5, 2009 at 20:49 Comment(7)
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.Hypersthene
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.Ribbentrop
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.Boyce
Furthermore, boost::program_options is not a header only library. You'll have to build boost. This is very troublesome.Mcbee
boost seems like a total overkill for this taskTimberlake
This answer is crying out for a simple example IMHO.Bimestrial
reddit.com/r/cpp/comments/4zhm2n/…Pontone
K
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; }
}
Khiva answered 10/10, 2011 at 11:54 Comment(10)
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.Perquisite
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.Abscond
Looks like the project moved to sourceforge.Hearn
@JohnShedletsky are you sure? I don't use the lib any more but in the manual are showed both long and short format arguments.Khiva
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.Career
@naugraghi Actually it does work but the error I was getting was misleading.Almucantar
Why does the distribution have a configuration script and a Makefile if the library is header-only?Rephrase
@Rephrase Opening the Makefile it seems to be there to run the tests, compile the docs and build the examples.Khiva
Didn't vote this one down, but I'm personally not a fan of something small like this having the dependency of exceptions.Forefinger
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.Kasten
S
44

Boost.Program_options

Sidestep answered 31/10, 2008 at 14:2 Comment(5)
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.Ribbentrop
Compiling code with Boost.Program_options did not seem straight-forward and required linking options, et cetera, beyond the inclusion of the header file.Perquisite
You can get pretty much the same for much less. If you want things like --long-option, it's fairly straightforward to do yourself.Flipper
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.Positronium
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.Pancho
O
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;
}
Odericus answered 14/5, 2009 at 20:49 Comment(5)
FYI, GNU getopt is GPL and getoptpp is also GPL so boost variant could be better for non open-source software.Rhiamon
@SorinSbarnea, TINLA, but I believe the license is actually LGPLv2.Odericus
Sorry but Google Code license on the project page states clearly GPL.Rhiamon
@SorinSbarnea, did you look at my link? I should have made it clearer, but I was referring to getopt and getopt-gnu, not getoptpp.Odericus
Getopt is stupidly low level for a C++ program. I highly recommend AGAINST using it for a C++ program.Barmecidal
P
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.

Perlis answered 31/10, 2008 at 14:12 Comment(3)
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.Barmecidal
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"Parham
here's an example example provided by the GNU itself ^^Judsonjudus
H
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.

Hinze answered 24/1, 2012 at 18:12 Comment(2)
Link for future reference: sourceforge.net/projects/optionparserBotheration
Incredibly error-prone, in my opinion, e. g. I received a segfault after segfault (in the lib code) while using itSanderling
M
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);
    }

}
Mukund answered 4/8, 2011 at 23:24 Comment(8)
-1: this fetches array elements without bound checksBlondy
-1: because of no bounds checkingChainsmoke
@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.Aqua
@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.Microminiaturization
-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.Lammond
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.Wicketkeeper
@Robert Munafo the asks for a way to parse the command line and additionally wether there is an STL-lib.Wreath
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]; ... }Anachronism
G
20

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

Grammarian answered 22/8, 2012 at 22:37 Comment(1)
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.Leaving
C
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.]

Crumpler answered 14/1, 2009 at 6:50 Comment(5)
Added a simple example. Really all the vector<string> buys you is simple comparisons with == and convenient by-value copying.Crumpler
This doesn't help to do anything that can't be already done with argc/argv.Sensualist
@brofield: Sure, it doesn't change the world. But I find the == and value semantics simplify things enough that I keep using it.Crumpler
@Crumpler - 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.Monjan
@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?Crumpler
S
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.

Streaming answered 30/9, 2010 at 0:35 Comment(1)
+1, didn't know about tclap and it manages to be lightweight and yet feels complete, I'm definitely going to delve deeper.Son
R
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.

Rhamnaceous answered 11/5, 2011 at 23:10 Comment(3)
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.Leaving
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.Justinjustina
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..Alfie
A
16

And there's a Google library available.

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

Arrogant answered 31/10, 2008 at 16:46 Comment(2)
(Thank you @QPaysTaxes for noticing the link was broken; I don't know why your edit was rejected, but you were definitely correct).Arrogant
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.Pescara
J
15

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

Boost.Program Options

Jumper answered 14/1, 2009 at 1:17 Comment(0)
C
11

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

Cheesecake answered 17/9, 2009 at 3:18 Comment(1)
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).Nickelic
K
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;
}
Koller answered 14/7, 2014 at 12:44 Comment(1)
What do you mean by "... is not too immediate to use"? Can you elaborate?Sodom
H
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.

Honshu answered 31/10, 2008 at 14:19 Comment(0)
A
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
Acaudal answered 3/10, 2012 at 6:11 Comment(4)
This looks pretty neat. Glad I scrolled down; there's just nothing very good for plain C, save this!Whatever
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.Sergu
@Sergu 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)...Acaudal
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.Sergu
O
6

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

Ornithine answered 17/9, 2009 at 3:19 Comment(0)
S
6

Google's gflags

Schermerhorn answered 17/9, 2009 at 3:20 Comment(1)
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.Nickelic
B
5

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

Burger answered 14/1, 2009 at 1:18 Comment(3)
or getopt_long if you've got itKiefer
I'm not sure if getopt is available on windows. I know it is in C on GNUGaudy
my co-worker used it on windows. im quite sure it's available on windowsExciter
S
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.

Sensualist answered 21/4, 2009 at 8:45 Comment(0)
A
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.

Amylase answered 17/8, 2011 at 15:23 Comment(4)
Similar to "The Lean Mean C++ Option Parser", but has a much better API. Highly recommended.Forefinger
It does not support any other OS but WindowsSanderling
@Sanderling What makes you say it's windows only?Amylase
It uses strcpy_s. I had some luck getting it to work on macos by writing my own quick-and-dirty strcpy_s that calls strcpy.Howbeit
S
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.

Saskatoon answered 12/1, 2014 at 10:56 Comment(0)
K
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.

Katabolism answered 15/9, 2016 at 8:8 Comment(0)
P
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.

Petition answered 31/10, 2008 at 14:26 Comment(0)
T
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.

Turki answered 14/5, 2009 at 20:50 Comment(0)
P
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.

Pelvic answered 3/5, 2010 at 10:54 Comment(0)
B
3

You could use an already created library for this

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

Barber answered 30/9, 2010 at 0:35 Comment(0)
D
2

Boost program_options.

Dionedionis answered 17/9, 2009 at 3:19 Comment(0)
P
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.

Pelvic answered 3/5, 2010 at 10:50 Comment(0)
G
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.

Growler answered 21/6, 2010 at 11:54 Comment(0)
T
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

Threadgill answered 30/9, 2010 at 0:37 Comment(3)
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.Streaming
it works fine in c++ tho; its what we use in all our c++ code.Threadgill
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.Streaming
E
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.

Eisenhower answered 20/4, 2011 at 19:31 Comment(2)
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))Sanderling
@Sanderling No idea, but this seems like an "inside" function, not part of the API. In case you have a doubt, better ask the author.Eisenhower
C
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.

Clarhe answered 14/1, 2014 at 10:52 Comment(0)
P
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.

Parade answered 15/9, 2016 at 8:57 Comment(0)
G
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.

Gesticulative answered 3/4, 2018 at 4:23 Comment(0)
H
0

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

Hannis answered 14/1, 2009 at 6:47 Comment(0)
P
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.

Pelvic answered 27/10, 2010 at 4:4 Comment(0)
P
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.
Petit answered 15/9, 2016 at 7:9 Comment(0)
P
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;
...
Polarization answered 12/12, 2017 at 11:32 Comment(0)
P
-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.

Persse answered 10/2, 2021 at 23:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.