argparse/README.md
Pranav Srinivas Kumar 4953ba7d76
Update README.md
2019-04-01 19:47:02 -04:00

280 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Argument Parser for Modern C++
## Highlights
* Header-only library
* Requires C++17
* MIT License
## Quick Start
Simply include argparse.hpp and you're good to go.
```cpp
#include <argparse.hpp>
```
To start parsing command-line arguments, create an ```ArgumentParser```.
```cpp
argparse::ArgumentParser program("program name");
```
Argparse supports a variety of argument types including positional, optional, and compound arguments.
### Positional Arguments
Here's an example of a ***positional argument***:
```cpp
program.add_argument("square")
.help("display the square of a given integer")
.action([](const std::string& value) { auto integer = std::stoi(value); return integer * integer; });
program.parse_args(argc, argv);
std::cout << program.get<int>("square") << std::endl;
```
And running the code:
```bash
$ ./main 15
225
```
Here's what's happening:
* The ```add_argument()``` method is used to specify which command-line options the program is willing to accept. In this case, Ive named it square so that its in line with its function.
* Command-line arguments are strings. Inorder to square the argument and print the result, we need to convert this argument to a number. In order to do this, we use the ```.action``` method and provide a lambda function that takes the argument value (std::string) and returns the square of the number it represents. Actions are quite powerful as you will see in later examples.
* Calling our program now requires us to specify an option.
* The ```parse_args()``` method parses the arguments provided, converts our input into an integer and returns the square.
* We can get the value stored by the parser for a given argument using ```parser.get<T>(key)``` method.
### Optional Arguments
Now, let's look at ***optional arguments***. Optional arguments start with ```-``` or ```--```, e.g., ```--verbose``` or ```-a```. Optional arguments can be placed anywhere in the input sequence.
```cpp
argparse::ArgumentParser program("test");
program.add_argument("--verbose")
.help("increase output verbosity")
.default_value(false)
.implicit_value(true);
program.parse_args(argc, argv);
if (program["--verbose"] == true) {
std::cout << "Verbosity enabled" << std::endl;
}
```
```bash
$ ./main --verbose
Verbosity enabled
```
Here's what's happening:
* The program is written so as to display something when --verbose is specified and display nothing when not.
* To show that the option is actually optional, there is no error when running the program without it. Note that by using ```.default_value(false)```, if the optional argument isnt used, it's value is automatically set to false.
* By using ```.implicit_value(true)```, the user specifies that this option is more of a flag than something that requires a value. When the user provides the --verbose option, it's value is set to true.
### Combining Positional and Optional Arguments
```cpp
argparse::ArgumentParser program("test");
program.add_argument("square")
.help("display the square of a given number")
.action([](const std::string& value) { return std::stoi(value); });
program.add_argument("--verbose")
.default_value(false)
.implicit_value(true);
program.parse_args(argc, argv);
int input = program.get<int>("square");
if (program["--verbose"] == true) {
std::cout << "The square of " << input << " is " << (input * input) << std::endl;
}
else {
std::cout << (input * input) << std::endl;
}
```
```bash
$ ./main 4
16
$ ./main 4 --verbose
The square of 4 is 16
$ ./main --verbose 4
The square of 4 is 16
```
### List of Arguments
ArgumentParser objects usually associate a single command-line argument with a single action to be taken. The ```.nargs``` associates a different number of command-line arguments with a single action. When using ```nargs(N)```, N arguments from the command line will be gathered together into a list.
```cpp
argparse::ArgumentParser program("main");
program.add_argument("--input_files")
.help("The list of input files")
.nargs(2);
program.parse_args({"./main", "--input_files", "config.yml", "System.xml"});
auto files = program.get<std::vector<std::string>>("--input_files"); // {"config.yml", "System.xml"}
```
### Compound Arguments
Compound arguments are optional arguments that are combined and provided as a single argument. Example: ```ps -aux```
```cpp
argparse::ArgumentParser program("test");
program.add_argument("-a")
.default_value(false)
.implicit_value(true);
program.add_argument("-b")
.default_value(false)
.implicit_value(true);
program.add_argument("-c")
.nargs(2)
.default_value(std::vector<float>{0.0f, 0.0f})
.action([](const std::string& value) { return std::stof(value); });
program.parse_args({ "./main", "-abc", "3.14", "2.718" });
auto a = program.get<bool>("-a"); // true
auto b = program.get<bool>("-b"); // true
auto c = program.get<std::vector<float>>("-c"); // {3.14f, 2.718f}
/// Some code that prints parsed arguments
```
```bash
$ ./main -ac 3.14 2.718
a = true
b = false
c = {3.14, 2.718}
$ ./main -cb
a = false
b = true
c = {0.0, 0.0}
```
Here's what's happening:
* We have three optional arguments ```-a```, ```-b``` and ```-c```.
* ```-a``` and ```-b``` are toggle arguments.
* ```-c``` requires 2 floating point numbers from the command-line. You can specify how many inputs to expect using ```nargs```.
* argparse can handle compound arguments, e.g., ```-abc``` or ```-bac``` or ```-cab```. This only works with short single-character argument names.
- ```-a``` and ```-b``` become true.
- argv is further parsed to identify the inputs mapped to ```-c```.
## Further Examples
### Construct a JSON object from a filename argument
```cpp
argparse::ArgumentParser program("json_test");
program.add_argument("config")
.action([](const std::string& value) {
// read a JSON file
std::ifstream stream(value);
nlohmann::json config_json;
stream >> config_json;
return config_json;
});
program.parse_args({"./test", "config.json"});
nlohmann::json config = program.get<nlohmann::json>("config");
```
### Positional Arguments with Compound Toggle Arguments
```cpp
argparse::ArgumentParser program("test");
program.add_argument("numbers")
.nargs(3)
.action([](const std::string& value) { return std::stoi(value); });
program.add_argument("-a")
.default_value(false)
.implicit_value(true);
program.add_argument("-b")
.default_value(false)
.implicit_value(true);
program.add_argument("-c")
.nargs(2)
.action([](const std::string& value) { return std::stof(value); });
program.add_argument("--files")
.nargs(3);
program.parse_args(argc, argv);
auto numbers = program.get<std::vector<int>>("numbers"); // {1, 2, 3}
auto a = program.get<bool>("-a"); // true
auto b = program.get<bool>("-b"); // true
auto c = program.get<std::vector<float>>("-c"); // {3.14f, 2.718f}
auto files = program.get<std::vector<std::string>>("--files"); // {"a.txt", "b.txt", "c.txt"}
/// Some code that prints parsed arguments
```
```bash
./main 1 -abc 3.14 2.718 2 --files a.txt b.txt c.txt 3
numbers = {1, 2, 3}
a = true
b = true
c = {3.14, 2.718}
d = {"a.txt", "b.txt", "c.txt"}
```
### Restricting the set of values for an argument
```cpp
argparse::ArgumentParser program("test");
program.add_argument("input")
.default_value("baz")
.action([=](const std::string& value) {
static const std::vector<std::string> choices = { "foo", "bar", "baz" };
if (std::find(choices.begin(), choices.end(), value) != choices.end()) {
return value;
}
return std::string{ "baz" };
});
program.parse_args({ "./test", "fez" });
auto input = program.get("input"); // baz
std::cout << input << std::endl;
```
```bash
$ ./main fex
baz
```
## Contributing
Contributions are welcomed, have a look at the [CONTRIBUTING.md](CONTRIBUTING.md) document for more information.
## License
The project is available under the [MIT](https://opensource.org/licenses/MIT) license.