Parsing command line arguments in C++
Note that my naming conventions differ a bit from the standart:
- What POSIX® calls "operands", are denoted as "unnamed arguments" in my code and comments.
- Arguments that follow after options are called "sub-arguments" in my code, instead of "option arguments".
To get it out of the way upfront, this library has some limitations:
- Doesn't allow the same argument to be passed multiple times.
- Doesn't allow the character
-
as a standalone argument.
But also some advantages:
- Lighter than boost or other alternative
- The above limitations allow for a much easier syntax when using the library
- It's fast
First, include the library:
#include "path/to/argparse.hpp"
The library introduces a struct called option
, which lets the user describe an expected command line option. It contains the following fields:
id
- used to give the option an unique identifier by which it can be retrieved after parsing.shortname
- the short option name that will be used after a single dash.longname
- the long option name that will be used after two dashes.has_sub_argument
- Does the option expect a sub-argument afterwards?
We first a vector of these options and fill it with the options we allow the user to pass to the program on the command line:
vector<option> options;
options.push_back({0, "i", "include", true});
options.push_back({1, "f", "force", false});
options.push_back({2, "r", "", false});
This will allow the user to write commands like the following:
prog -i fileA --force -r
prog -rfi fileA
These two lines are equivalent.
To tell the parser to look for an unnamed argument (in POSIX® terms: operand), we have to create an option
with the shortname
and longname
both set to "":
options.push_back({3, "", "", true}); // note that the field has_sub_argument is ignored in this case
This will allow for commands like the following:
prog -i fileA output.txt
prog output.txt -i fileA
And again, the above two lines are equivalent. Note that the order of arguments only ever plays a role for multiple unnamed options, otherwise all allowed arrangements are equivalent.
After setting up the options, we can now run the parser with the following command:
unordered_map<int, string> result = argparse(options, argc, argv);
The unordered_map result
will contain the keys of all the options that occurred in the arguments. If the option requires a sub-argument, the value assiciated to the key will contain the sub-argument. To test this out, we could print the resulting unordered_map
like this:
for(auto r:result){
cout << r.first << "-" << r.second << "\n";
}
To compile anything with argparse, we first have to run the makefile in the argparse directory:
make -C path/to/argparse
Then, we can compile the program. Assuming that the code is saved in prog.cpp
and that the file argparse.o
is in the subdirectory argparse/
:
g++ -std=c++11 -o prog prog.cpp argparse/argparse.o
After that, we can run the program like this:
$ ./prog -i fileA output.txt
0 - fileA
3 - output
$ ./prog -ri fileA
0 - fileA
2 -
In case of an error, argparse
prints an error message to stderr and returns an unordered_map with just a single key-value pair: {-1, "ERROR"}
.