Argument Parser for Modern C++

Overview

argparse

travis license version

Highlights

  • Single header file
  • Requires C++17
  • MIT License

Quick Start

Simply include argparse.hpp and you're good to go.

#include <argparse/argparse.hpp>

To start parsing command-line arguments, create an ArgumentParser.

argparse::ArgumentParser program("program name");

NOTE: There is an optional second argument to the ArgumentParser which is the program version. Example: argparse::ArgumentParser program("libfoo", "1.9.0");

To add a new argument, simply call .add_argument(...). You can provide a variadic list of argument names that you want to group together, e.g., -v and --verbose

program.add_argument("foo");
program.add_argument("-v", "--verbose"); // parameter packing

Argparse supports a variety of argument types including positional, optional, and compound arguments. Below you can see how to configure each of these types:

Positional Arguments

Here's an example of a positional argument:

#include <argparse/argparse.hpp>

int main(int argc, char *argv[]) {
  argparse::ArgumentParser program("program name");

  program.add_argument("square")
    .help("display the square of a given integer")
    .scan<'i', int>();

  try {
    program.parse_args(argc, argv);
  }
  catch (const std::runtime_error& err) {
    std::cout << err.what() << std::endl;
    std::cout << program;
    exit(0);
  }

  auto input = program.get<int>("square");
  std::cout << (input * input) << std::endl;

  return 0;
}

And running the code:

$ ./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, I’ve named it square so that it’s in line with its function.
  • Command-line arguments are strings. 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 .scan method to convert user input into an integer.
  • 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.

argparse::ArgumentParser program("test");

program.add_argument("--verbose")
  .help("increase output verbosity")
  .default_value(false)
  .implicit_value(true);

try {
  program.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

if (program["--verbose"] == true) {
    std::cout << "Verbosity enabled" << std::endl;
}
$ ./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.
  • Since the argument is actually optional, no error is thrown when running the program without --verbose. Note that by using .default_value(false), if the optional argument isn’t 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.

Requiring optional arguments

There are scenarios where you would like to make an optional argument required. As discussed above, optional arguments either begin with - or --. You can make these types of arguments required like so:

program.add_argument("-o", "--output")
  .required()
  .help("specify the output file.");

If the user does not provide a value for this parameter, an exception is thrown.

Alternatively, you could provide a default value like so:

program.add_argument("-o", "--output")
  .default_value(std::string("-"))
  .required()
  .help("specify the output file.");

Accessing optional arguments without default values

If you require an optional argument to be present but have no good default value for it, you can combine testing and accessing the argument as following:

if (auto fn = program.present("-o")) {
    do_something_with(*fn);
}

Similar to get, the present method also accepts a template argument. But rather than returning T, parser.present<T>(key) returns std::optional<T>, so that when the user does not provide a value to this parameter, the return value compares equal to std::nullopt.

Deciding if the value was given by the user

If you want to know whether the user supplied a value for an argument that has a .default_value, check whether the argument .is_used().

program.add_argument("--color")
  .default_value("orange")
  .help("specify the cat's fur color");

try {
  program.parse_args(argc, argv);    // Example: ./main --color orange
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

auto color = program.get<std::string>("--color");  // "orange"
auto explicit_color = program.is_used("--color");  // true, user provided orange

Joining values of repeated optional arguments

You may want to allow an optional argument to be repeated and gather all values in one place.

program.add_argument("--color")
  .default_value<std::vector<std::string>>({ "orange" })
  .append()
  .help("specify the cat's fur color");

try {
  program.parse_args(argc, argv);    // Example: ./main --color red --color green --color blue
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

auto colors = program.get<std::vector<std::string>>("--color");  // {"red", "green", "blue"}

Notice that .default_value is given an explicit template parameter to match the type you want to .get.

Negative Numbers

Optional arguments start with -. Can argparse handle negative numbers? The answer is yes!

argparse::ArgumentParser program;

program.add_argument("integer")
  .help("Input number")
  .scan<'i', int>();

program.add_argument("floats")
  .help("Vector of floats")
  .nargs(4)
  .scan<'g', float>();

try {
  program.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

// Some code to print arguments
$ ./main -5 -1.1 -3.1415 -3.1e2 -4.51329E3
integer : -5
floats  : -1.1 -3.1415 -310 -4513.29

As you can see here, argparse supports negative integers, negative floats and scientific notation.

Combining Positional and Optional Arguments

argparse::ArgumentParser program("test");

program.add_argument("square")
  .help("display the square of a given number")
  .scan<'i', int>();

program.add_argument("--verbose")
  .default_value(false)
  .implicit_value(true);

try {
  program.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

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;
}
$ ./main 4
16

$ ./main 4 --verbose
The square of 4 is 16

$ ./main --verbose 4
The square of 4 is 16

Printing Help

std::cout << program prints a help message, including the program usage and information about the arguments registered with the ArgumentParser. For the previous example, here's the default help message:

$ ./main --help
Usage: ./main [options] square

Positional arguments:
square         display a square of a given number

Optional arguments:
-h, --help     show this help message and exit
-v, --verbose  enable verbose logging

You may also get the help message in string via program.help().str().

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.

argparse::ArgumentParser program("main");

program.add_argument("--input_files")
  .help("The list of input files")
  .nargs(2);

try {
  program.parse_args(argc, argv);   // Example: ./main --input_files config.yml System.xml
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

auto files = program.get<std::vector<std::string>>("--input_files");  // {"config.yml", "System.xml"}

ArgumentParser.get<T>() has specializations for std::vector and std::list. So, the following variant, .get<std::list>, will also work.

auto files = program.get<std::list<std::string>>("--input_files");  // {"config.yml", "System.xml"}

Using .scan, one can quickly build a list of desired value types from command line arguments. Here's an example:

argparse::ArgumentParser program("main");

program.add_argument("--query_point")
  .help("3D query point")
  .nargs(3)
  .default_value(std::vector<double>{0.0, 0.0, 0.0})
  .scan<'g', double>();

try {
  program.parse_args(argc, argv); // Example: ./main --query_point 3.5 4.7 9.2
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

auto query_point = program.get<std::vector<double>>("--query_point");  // {3.5, 4.7, 9.2}

Compound Arguments

Compound arguments are optional arguments that are combined and provided as a single argument. Example: ps -aux

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})
  .scan<'g', float>();

try {
  program.parse_args(argc, argv);                  // Example: ./main -abc 1.95 2.47
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

auto a = program.get<bool>("-a");                  // true
auto b = program.get<bool>("-b");                  // true
auto c = program.get<std::vector<float>>("-c");    // {1.95, 2.47}

/// Some code that prints parsed arguments
$ ./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.
  • 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.
    • If argparse cannot find any arguments to map to c, then c defaults to {0.0, 0.0} as defined by .default_value

Converting to Numeric Types

For inputs, users can express a primitive type for the value.

The .scan<Shape, T> method attempts to convert the incoming std::string to T following the Shape conversion specifier. An std::invalid_argument or std::range_error exception is thrown for errors.

program.add_argument("-x")
       .scan<'d', int>();

program.add_argument("scale")
       .scan<'g', double>();

Shape specifies what the input "looks like", and the type template argument specifies the return value of the predefined action. Acceptable types are floating point (i.e float, double, long double) and integral (i.e. signed char, short, int, long, long long).

The grammar follows std::from_chars, but does not exactly duplicate it. For example, hexadecimal numbers may begin with 0x or 0X and numbers with a leading zero may be handled as octal values.

Shape interpretation
'a' or 'A' hexadecimal floating point
'e' or 'E' scientific notation (floating point)
'f' or 'F' fixed notation (floating point)
'g' or 'G' general form (either fixed or scientific)
'd' decimal
'i' std::from_chars grammar with base == 0
'o' octal (unsigned)
'u' decimal (unsigned)
'x' or 'X' hexadecimal (unsigned)

Gathering Remaining Arguments

argparse supports gathering "remaining" arguments at the end of the command, e.g., for use in a compiler:

$ compiler file1 file2 file3

To enable this, simply create an argument and mark it as remaining. All remaining arguments passed to argparse are gathered here.

argparse::ArgumentParser program("compiler");

program.add_argument("files")
  .remaining();

try {
  program.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

try {
  auto files = program.get<std::vector<std::string>>("files");
  std::cout << files.size() << " files provided" << std::endl;
  for (auto& file : files)
    std::cout << file << std::endl;
} catch (std::logic_error& e) {
  std::cout << "No files provided" << std::endl;
}

When no arguments are provided:

$ ./compiler
No files provided

and when multiple arguments are provided:

$ ./compiler foo.txt bar.txt baz.txt
3 files provided
foo.txt
bar.txt
baz.txt

The process of gathering remaining arguments plays nicely with optional arguments too:

argparse::ArgumentParser program("compiler");

program.add_arguments("-o")
  .default_value(std::string("a.out"));

program.add_argument("files")
  .remaining();

try {
  program.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

auto output_filename = program.get<std::string>("-o");
std::cout << "Output filename: " << output_filename << std::endl;

try {
  auto files = program.get<std::vector<std::string>>("files");
  std::cout << files.size() << " files provided" << std::endl;
  for (auto& file : files)
    std::cout << file << std::endl;
} catch (std::logic_error& e) {
  std::cout << "No files provided" << std::endl;
}
$ ./compiler -o main foo.cpp bar.cpp baz.cpp
Output filename: main
3 files provided
foo.cpp
bar.cpp
baz.cpp

NOTE: Remember to place all optional arguments BEFORE the remaining argument. If the optional argument is placed after the remaining arguments, it too will be deemed remaining:

$ ./compiler foo.cpp bar.cpp baz.cpp -o main
5 arguments provided
foo.cpp
bar.cpp
baz.cpp
-o
main

Parent Parsers

Sometimes, several parsers share a common set of arguments. Rather than repeating the definitions of these arguments, a single parser with all the common arguments can be added as a parent to another ArgumentParser instance. The .add_parents method takes a list of ArgumentParser objects, collects all the positional and optional actions from them, and adds these actions to the ArgumentParser object being constructed:

argparse::ArgumentParser parent_parser("main");
parent_parser.add_argument("--parent")
  .default_value(0)
  .scan<'i', int>();

argparse::ArgumentParser foo_parser("foo");
foo_parser.add_argument("foo");
foo_parser.add_parents(parent_parser);
foo_parser.parse_args({ "./main", "--parent", "2", "XXX" });   // parent = 2, foo = XXX

argparse::ArgumentParser bar_parser("bar");
bar_parser.add_argument("--bar");
bar_parser.parse_args({ "./main", "--bar", "YYY" });           // bar = YYY

Note You must fully initialize the parsers before passing them via .add_parents. If you change the parent parsers after the child parser, those changes will not be reflected in the child.

Further Examples

Construct a JSON object from a filename argument

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;
  });

try {
  program.parse_args({"./test", "config.json"});
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

nlohmann::json config = program.get<nlohmann::json>("config");

Positional Arguments with Compound Toggle Arguments

argparse::ArgumentParser program("test");

program.add_argument("numbers")
  .nargs(3)
  .scan<'i', int>();

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)
  .scan<'g', float>();

program.add_argument("--files")
  .nargs(3);

try {
  program.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

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
$ ./main 1 2 3 -abc 3.14 2.718 --files a.txt b.txt c.txt
numbers = {1, 2, 3}
a = true
b = true
c = {3.14, 2.718}
files = {"a.txt", "b.txt", "c.txt"}

Restricting the set of values for an argument

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" };
  });

try {
  program.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
  std::cout << err.what() << std::endl;
  std::cout << program;
  exit(0);
}

auto input = program.get("input");
std::cout << input << std::endl;
$ ./main fex
baz

Supported Toolchains

Compiler Standard Library Test Environment
GCC >= 8.3.0 libstdc++ Ubuntu 18.04
Clang >= 7.0.0 libc++ Xcode 10.2
MSVC >= 14.16 Microsoft STL Visual Studio 2017

Contributing

Contributions are welcome, have a look at the CONTRIBUTING.md document for more information.

Contributors

Thanks goes to these wonderful people:

svanveen
svanveen
Zhihao Yuan
Zhihao Yuan
Mio
Mio
zhihaoy
zhihaoy
Jack Clarke
Jack Clarke
Daniel Marshall
Daniel Marshall
mupp
mupp
Ethan Slattery
Ethan Slattery

License

The project is available under the MIT license.

Issues
  • Fixed issue #119 and a few other minor code improvements.

    Fixed issue #119 and a few other minor code improvements.

    This PR fixes the issue #119 , and also makes a few minor code readability and performance improvements discussed as follows:

    1. The argparse::ArgumentParser::parse_args_validate() method uses a for each loop instead of std::for_each as it is more readable and performs the same task.

    2. The argparse::ArgumentParser::get_length_of_longest_argument() method now does not make use of an std::vector and runs in a better O(1) space complexity.

    3. The issue of printing overriden args (which btw is the issue #119 ), has been fixed and it now prints the overriden/ updated values.

    Before: image

    After: image

    opened by aayush749 16
  • conan package 2.1 does not match 2.1 tag

    conan package 2.1 does not match 2.1 tag

    related to my comment here: https://github.com/p-ranav/argparse/issues/2 conan package https://conan.io/center/argparse does not match: https://github.com/p-ranav/argparse/tree/v2.1 for example, it does not have "scan" function. anything that I am missing? I basically cannot do something very simple as:

        program.add_argument("--something")
            .default_value(0);
    

    if I pass --something 10 in the cmd line arg, I'm getting bad cast when trying to interpret it as int:

    program.get<int>("--something");
    

    It is as if I need to have all std::any be strings and then manually convert it to ints. I need something like boost which has the .as<T>()

    Any pointers are appreciated. thanks

    opened by kobi-ca 14
  • argparse.hpp doesn't compile : 2 errors thrown

    argparse.hpp doesn't compile : 2 errors thrown

    Hello, I might have done something wrong so i ask you what i could have done wrong. I first copy your argparse.hpp file in my project, and called it. When I try to compile the project, two errors are thrown :

    • no matching function for call to ‘argparse::Argument::get<char [1]>() const’ (gcc [Ln 586, Col23])
    • function returning an array (gcc, [Ln 777,Col27] Did I miss something ? Thank you for your help. Have a nice day. (Sorry for my english)
    opened by Chabname 7
  • Printing program version

    Printing program version

    Python's argparse has a special action that prints the program version and exits.

    Currently printing the program version with this library can be done, but the presence of required arguments will also cause an error.

    Support for printing the program version would be nice to have as it's an expected part of any CLI, akin to the --help option.

    opened by ekmecic 5
  • Merged too fast.

    Merged too fast.

    I made a mistake. $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/include)

    Should be CMAKE_CURRENT_LIST_DIR not SOURCE_DIR. Literally just fixed it as you merged lol.

    opened by SuperWig 5
  • Use .action() with int/float arguments

    Use .action() with int/float arguments

    Hello!

    I am very new to programming in c++ so at first I don't know if I'm doing something wrong. But, I am trying to check whether an argument meets certain parameters and I can't manage to do it with the action() option.

    For example, this works fine:

    program.add_argument("-w, --workDir")
    .help("Main directory")
    .default_value("/home")
    .action([](const std::string& value) {
    	if (!(fs::exists(value) & fs::is_directory(value))){
    		std::cerr << "\nERROR: The directory provided does not exist\n" << std::endl;
    		std::exit(1);
    	} else {
    		return value;
    	}
    });
    

    However if I try to extrapolate this with numbers it doesn't work:

    program.add_argument("--numeric_argument")
    .help("Numeric example")
    .scan<'g', float>()
    .default_value(float(0.05))
    .action([](const float& value) {
    	if (value < 0.0 | value > 1.0) {
    		std::cerr << "\nERROR: Numerical argument must be between 0 and 1\n" << std::endl;
    	} else {
    		return value;
    	}
    });
    

    With the code above I get the following error during compilation g++ -std=c++17 -o prueba prueba.cpp -lstdc++fs:

    prueba.cpp: In function ‘int main(int, char**)’:
    prueba.cpp:111:4: error: no matching function for call to ‘argparse::Argument::action(main(int, char**)::<lambda(const float&)>)’
       });
        ^
    In file included from prueba.cpp:1:
    /usr/local/include/argparse.hpp:372:8: note: candidate: ‘template<class F, class ... Args> std::enable_if_t<is_invocable_v<F, Args ..., const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, argparse::Argument&> argparse::Argument::action(F&&, Args&& ...)’
       auto action(F &&aAction, Args &&... aBound)
            ^~~~~~
    /usr/local/include/argparse.hpp:372:8: note:   template argument deduction/substitution failed:
    

    I know how to do it outside the add_argument() function, but is it possible to do something similar to what I have written inside the action() function?

    Thank you very much! Ander

    opened by anderdnavarro 4
  • Const-correct ArgumentParser

    Const-correct ArgumentParser

    Hi!

    I wished to provide a small contribution to your fine project.

    In another project I am currently working on, I stumbled upon a minor inconvenience in argparse's interface. Normally after parsing the parameters one would expect the parser to be immutable (quite understandably so in simple cases). However, I realized a crucial function checking for a value being actually passed by the user is not, though could be, const!

    I am concerned the test I wrote might actually be meaningless. What one would expect there is for the code to actually compile, nothing more, to validate this change. I could also offer rewriting some tests to try to better comply with ArgumentParser's immutability at some point in their bodies, if you deem that viable or useful in any way.

    Cheers!

    opened by Bedzior 4
  • Can you create releases?

    Can you create releases?

    I'm trying to use this library through hunter package management system. Would you mind creating some releases once in a while to make it easier to track?

    thanks! Marco

    opened by marcovc 4
  • Default Value Returns Bad Any Cast

    Default Value Returns Bad Any Cast

    Hello,

    Running this with clang on Mac OS, when trying to use the library as such:

    argparse::ArgumentParser parser{"program"};
    parser.add_argument("-t", "--test")
               .help("Blah")
               .default_value("test");
    
    parser.parse_args(arc, argv);
    . . .
    
    parser.get<std::string>("-t"); // Bad any_cast
    

    It seems that the compiler thinks that "test" is of type const char[5]. I tried parser.get<char*>("-t") with no luck as well. My only workaround was to statically cast within the default value .default_value(static_cast<std::string>("test")). I realize that this default value arg is getting any casted, but maybe the default value should be templated instead?

    opened by xander-io 4
  • Failing tests

    Failing tests

    These tests fail:

    #include <test_parse_args.hpp>
    #include <test_positional_arguments.hpp>
    #include <test_compound_arguments.hpp>
    #include <test_container_arguments.hpp>
    

    Here are the errors and warnings. I'm using Visual Studio 2019 and I get similar errors in Ubuntu 19.04, GCC 8.3

    argparse.hpp(444,1): warning C4456:  declaration of 'tCurrentArgument' hides previous local declaration
    argparse.hpp(423,21): message :  see declaration of 'tCurrentArgument'
    argparse.hpp(445,1): warning C4456:  declaration of 'tIterator' hides previous local declaration
    argparse.hpp(434,23): message :  see declaration of 'tIterator'
    argparse.hpp(132,23): warning C4018:  '<=': signed/unsigned mismatch
    argparse.hpp(432): message :  see reference to function template instantiation 'Iterator argparse::Argument::consume<_InIt>(Iterator,Iterator,std::string)' being compiled
    argparse.hpp(432): message :         with
    argparse.hpp(432): message :         [
    argparse.hpp(432): message :             Iterator=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::string>>>,
    argparse.hpp(432): message :             _InIt=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::string>>>
    argparse.hpp(432): message :         ]
    argparse.hpp(255,14): error C2672:  'std::transform': no matching overloaded function found
    argparse.hpp(361): message :  see reference to function template instantiation 'std::vector<int,std::allocator<int>> argparse::Argument::get<T>(void) const' being compiled
    argparse.hpp(361): message :         with
    argparse.hpp(361): message :         [
    argparse.hpp(361): message :             T=std::vector<int,std::allocator<int>>
    argparse.hpp(361): message :         ]
    argparse\test\test_parse_args.hpp(77): message :  see reference to function template instantiation 'T argparse::ArgumentParser::get<std::vector<int,std::allocator<int>>>(const std::string &)' being compiled
    argparse\test\test_parse_args.hpp(77): message :         with
    argparse\test\test_parse_args.hpp(77): message :         [
    argparse\test\test_parse_args.hpp(77): message :             T=std::vector<int,std::allocator<int>>
    argparse\test\test_parse_args.hpp(77): message :         ]
    argparse.hpp(256,1): error C2783:  '_OutIt std::transform(const _InIt,const _InIt,_OutIt,_Fn)': could not deduce template argument for '_Fn'
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.21.27702\include\algorithm(1314): message :  see declaration of 'std::transform'
    argparse.hpp(261,14): error C2672:  'std::transform': no matching overloaded function found
    argparse.hpp(262,1): error C2783:  '_OutIt std::transform(const _InIt,const _InIt,_OutIt,_Fn)': could not deduce template argument for '_Fn'
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.21.27702\include\algorithm(1314): message :  see declaration of 'std::transform'
    
    opened by ajpmaclean 4
  • Allow many positional arguments

    Allow many positional arguments

    It would be nice to be able to have a feature whereby you could gather many positional arguments at the end of a command for something like a compiler.

    For example, imagine a use case like this:

    compiler file1 file2 file3
    

    Currently I don't believe it is possible to do this.

    I imagine using it would look something like:

    std::vector<std::string> = program.get_trailing();
    

    which would just return all positional arguments at the end regardless of how many.

    enhancement 
    opened by Jackojc 4
  • Parser using parent parser shows help message for parent parser instead of own message

    Parser using parent parser shows help message for parent parser instead of own message

    Hi!

    I'm using parent parsers with argparse, but it seems that, when used, the automatic -h,--help and -v,--version options no longer exist in the "child" parser.

    int main(int argc, char *argv[])
    {
        argparse::ArgumentParser parent_args("parent");
        parent_args.add_argument("-b").help("board").required().scan<'d', int>();
    
        argparse::ArgumentParser child("child");
        decode_args.add_parents(parent_args);
    
        try {
            child.parse_args(argc, argv);
        } catch (const std::runtime_error &err) {
            fprintf(stderr, "argparse error: %s\n", err.what());
            return 1;
        }
    }
    

    Passing -b <value> works just fine, but not -h or -v, nor their long versions. If I use the parent parser though, everything works.

    I get the following error:

    $ ./parent -h
    argparse error: -h: expected 0 argument(s). 1 provided.
    
    opened by ericonr 6
  • Fix generic_strtod for clang-cl

    Fix generic_strtod for clang-cl

    Fixed to use explicit template specialization that call the function instead of being a function pointer. The end result is the exact same.

    Fixes https://github.com/p-ranav/argparse/issues/136

    opened by marstaik 2
  • Run actions with defaults values

    Run actions with defaults values

    Hi!

    Is there any way to execute an action if the user doesn't use the argument, only with the default value. For example:

    program.add_argument("-p")
        .help("FILE\tThis is an example")
        .default_value(std::string("ABCD"))
        .action([](const std::string& value) {
            static const std::vector<std::string> choices = { "ABCD", "EFGH" };
            if (std::find(choices.begin(), choices.end(), value) != choices.end()) {
                if ( value == "ABCD" ) {
                    return std::string("/home/test/this_file.txt");
                } else {
                    return std::string("/home/test/this_other_file.txt");
                }
            } 
    });
    

    With this example, if I run -p ABCD it works, but if I don't specify anything it doesn't work.

    I know this can be done later, outside your function, but I think it's cleaner if I can do something like that.

    Thank you very much for your help!!! Ander

    opened by anderdnavarro 3
  • This library is not

    This library is not "reentrable", instances of ArgumentParser are not reusable

    ArgumentParser::parse_args cannot be used multiple times and it would fail to detect missingness of mandatory positional args. Because of that I have to split tests testing if mandatory positional args work correctly into multiple tests. It is not very convenient.

    opened by KOLANICH 1
  • Project using argparse won't compile using clang-cl

    Project using argparse won't compile using clang-cl

    Relevant info:

    • IDE: Visual Studio 2022 Preview 4
    • OS: Windows 10 21H1 (Build 19043.1237)
    • Build System: CMake
    • Compiler: clang-cl
    • CXX Standard: C++17

    Errors

    PROJECT_DIR\lib\thirdparty\argparse\include\argparse\argparse.hpp(242,28): error : constexpr variable 'generic_strtod<float>' must be initialized by a constant expression
      template <> constexpr auto generic_strtod<float> = strtof;
                                 ^                       ~~~~~~
    PROJECT_DIR\lib\thirdparty\argparse\include\argparse\argparse.hpp(243,28): error : constexpr variable 'generic_strtod<double>' must be initialized by a constant expression
      template <> constexpr auto generic_strtod<double> = strtod;
                                 ^                        ~~~~~~
    PROJECT_DIR\lib\thirdparty\argparse\include\argparse\argparse.hpp(244,28): error : constexpr variable 'generic_strtod<long double>' must be initialized by a constant expression
      template <> constexpr auto generic_strtod<long double> = strtold;
                                 ^                             ~~~~~~~
    

    Build command (Ran by VS)

    clang-cl.exe  /nologo -TP  -IC:PROJECT_DIR\lib\thirdparty\argparse\include -m64 -fdiagnostics-absolute-paths  /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /showIncludes /Fosrc\CMakeFiles\todo.dir\main.cpp.obj /Fdsrc\CMakeFiles\todo.dir\ -c -- PROJECT_DIR\src\main.cpp
    

    CMakeLists.txt (@ Project root)

    cmake_minimum_required (VERSION 3.16)
    project ("PROJECT_NAME")
    set(CMAKE_CXX_STANDARD 17)
    
    add_subdirectory(src)
    add_subdirectory(lib/thirdparty/argparse)
    

    CMakeLists.txt (@ Project root/src/)

    add_executable (executable_name "main.cpp")
    target_include_directories(executable_name PRIVATE "${CMAKE_SOURCE_DIR}/lib/thirdparty/argparse/include/")
    
    opened by HFDan 4
Releases(v2.4)
  • v2.4(Apr 20, 2022)

    • Fixed regression in version printing https://github.com/p-ranav/argparse/commit/3b89546fd09daf69458d8502b384ac536a3ec6b9
    • Updated doctest version from v2.3.5 to v2.4.8 https://github.com/p-ranav/argparse/commit/f56aec307fb441d3affcbef70875b397f8cd4156
    • Fixed unused argument warnings https://github.com/p-ranav/argparse/commit/5c5c55b83cc7d542890a47aeee6530a8ee6d10f7
    Source code(tar.gz)
    Source code(zip)
  • v2.3(Apr 2, 2022)

    • Improve thrown message in case of invalid argument - Now message contains information which argument is the source of error. It's easier to spot typo/understand which part of more complex command is the source of problem. https://github.com/p-ranav/argparse/commit/87afaba6ba3fca3e0e3c3aca9bebe0a94f294d25
    • Update cmake_minimum_required to 3.12.4
    • Update "Printing Help" documentation https://github.com/p-ranav/argparse/commit/5cceb98e3c1f5cf23d3323853062e4b59086381b
    • Updated README examples from exit() to std::exit() https://github.com/p-ranav/argparse/commit/8772b37aabf9c3679c6b346c113fc7b82a247452
    • Code cleanup - data structure names, initialization etc.
    Source code(tar.gz)
    Source code(zip)
  • v2.2(Sep 14, 2021)

    • Simplify parsing numeric arguments with .scan #65
    • Get arguments in optional with .present() #68
    • Simplify a few internals #71
    • Add the option to add text before and after help output #72
    • Avoid use of cmd.exe in Travis #77
    • Fix incorrect message when mUsedName is empty #79
    • Make ArgumentParser::add_*() functions working on the parser itself chainable #82
    • CMakeLists.txt : add export #86
    • Fix help if required and def-value. Fixes #89. #90
    • Show default value for arg in help message #92
    • nicer usage text for required arg #93
    • Qualify iterator functions #97
    • misc clean ups #98
    • Add Argument.append method to allow repeated argument use #99
    • Add ArgumentParser.is_used to discern user-supplied values from defaults #100
    • Allow user to limit version argument to --version #103
    • Added packaging using CPack and generation of pkg-config files. #107
    • Const-correct ArgumentParser #108
    • Fix std::min conflict with min/max definitions from windows.h #109
    • Replace size_t to std::size_t. #110
    • Some cleanup in CPack packaging #115
    • Document and use Argument.scan where possible #121
    Source code(tar.gz)
    Source code(zip)
  • v2.0(Nov 21, 2019)

    • Added support for gathering remaining arguments #17
    • Added support for actions without return values #39
    • Deprecated print_help() #40
    • Removed undocumented PARSE_ARGS macro #41
    • Added .clang-format #43
    • Switched to value semantics with #50 and #51
    • Switched to doctest and parallel builds #53
    • Arguments which start with '-' could be positional arguments #56
    Source code(tar.gz)
    Source code(zip)
    argparse.hpp(20.84 KB)
  • v1.9(Aug 17, 2019)

    • Support for required arguments: https://github.com/p-ranav/argparse/pull/33 and https://github.com/p-ranav/argparse/pull/34
    • Updated README for required arguments: https://github.com/p-ranav/argparse/issues/35
    Source code(tar.gz)
    Source code(zip)
  • v1.8(Jun 9, 2019)

  • v1.5(May 12, 2019)

  • v1.4(May 10, 2019)

  • v1.2(May 2, 2019)

  • v1.0(Apr 11, 2019)

    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.

    #include <argparse.hpp>
    

    To start parsing command-line arguments, create an ArgumentParser.

    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:

    #include <argparse.hpp>
    
    int main(int argc, char *argv[]) {
      argparse::ArgumentParser program("program name");
    
      program.add_argument("square")
        .help("display the square of a given integer")
        .action([](const std::string& value) { return std::stoi(value); });
    
      program.parse_args(argc, argv);
      
      auto input = program.get<int>("square");
      std::cout << (input * input) << std::endl;
    
      return 0;
    }
    

    And running the code:

    $ ./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, I’ve named it square so that it’s 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 tries to convert user input into an integer.
    • 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.

    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;
    }
    
    $ ./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.
    • Since the argument is actually optional, no error is thrown when running the program without --verbose. Note that by using .default_value(false), if the optional argument isn’t 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

    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;
    }
    
    $ ./main 4
    16
    
    $ ./main 4 --verbose
    The square of 4 is 16
    
    $ ./main --verbose 4
    The square of 4 is 16
    

    Printing Help

    ArgumentParser.print_help() print a help message, including the program usage and information about the arguments registered with the ArgumentParser. For the previous example, here's the default help message:

    $ ./main --help
    Usage: ./main [options] square
    
    Positional arguments:
    square         display a square of a given number
    
    Optional arguments:
    -h, --help     show this help message and exit
    -v, --verbose  enable verbose logging
    

    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.

    argparse::ArgumentParser program("main");
    
    program.add_argument("--input_files")
      .help("The list of input files")
      .nargs(2);
    
    program.parse_args(argc, argv);  // Example: ./main --input_files config.yml System.xml
    
    auto files = program.get<std::vector<std::string>>("--input_files");  // {"config.yml", "System.xml"}
    

    ArgumentParser.get<T>() has specializations for std::vector and std::list. So, the following variant, .get<std::list>, will also work.

    auto files = program.get<std::list<std::string>>("--input_files");  // {"config.yml", "System.xml"}
    

    Using .action, one can quickly build a list of desired value types from command line arguments. Here's an example:

    argparse::ArgumentParser program("main");
    
    program.add_argument("--query_point")
      .help("3D query point")
      .nargs(3)
      .default_value(std::vector<double>{0.0, 0.0, 0.0})
      .action([](const std::string& value) { return std::stod(value); });
    
    program.parse_args(argc, argv);  // Example: ./main --query_point 3.5 4.7 9.2
    
    auto query_point = program.get<std::vector<double>>("--query_point");  // {3.5, 4.7, 9.2}
    

    Compound Arguments

    Compound arguments are optional arguments that are combined and provided as a single argument. Example: ps -aux

    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(argc, argv);                    // Example: ./main -abc 1.95 2.47 
    
    auto a = program.get<bool>("-a");                  // true
    auto b = program.get<bool>("-b");                  // true
    auto c = program.get<std::vector<float>>("-c");    // {1.95, 2.47}
    
    /// Some code that prints parsed arguments
    
    $ ./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.
    • 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.
      • If argparse cannot find any arguments to map to c, then c defaults to {0.0, 0.0} as defined by .default_value

    Parent Parsers

    Sometimes, several parsers share a common set of arguments. Rather than repeating the definitions of these arguments, a single parser with all the shared arguments can be added as a parent to another ArgumentParser instance. The .add_parents method takes a list of ArgumentParser objects, collects all the positional and optional actions from them, and adds these actions to the ArgumentParser object being constructed:

    argparse::ArgumentParser parent_parser("main");
    parent_parser.add_argument("--parent")
      .default_value(0)
      .action([](const std::string& value) { return std::stoi(value); });
    
    argparse::ArgumentParser foo_parser("foo");
    foo_parser.add_argument("foo");
    foo_parser.add_parents(parent_parser);
    foo_parser.parse_args({ "./main", "--parent", "2", "XXX" });   // parent = 2, foo = XXX
    
    argparse::ArgumentParser bar_parser("bar");
    bar_parser.add_argument("--bar");
    bar_parser.parse_args({ "./main", "--bar", "YYY" });           // bar = YYY
    

    Note You must fully initialize the parsers before passing them via .add_parents. If you change the parent parsers after the child parser, those changes will not be reflected in the child.

    Further Examples

    Construct a JSON object from a filename argument

    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

    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
    
    $ ./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

    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(argc, argv);
    
    auto input = program.get("input");
    std::cout << input << std::endl;
    
    $ ./main fex
    baz
    

    Supported Compilers

    • GCC >= 7.0.0
    • Clang >= 4.0
    • MSVC >= 2017

    Contributing

    Contributions are welcomed, have a look at the CONTRIBUTING.md document for more information.

    License

    The project is available under the MIT license.

    Source code(tar.gz)
    Source code(zip)
    argparse.hpp(25.13 KB)
Owner
Pranav
Pranav
A simple header-only C++ argument parser library. Supposed to be flexible and powerful, and attempts to be compatible with the functionality of the Python standard argparse library (though not necessarily the API).

args Note that this library is essentially in maintenance mode. I haven't had the time to work on it or give it the love that it deserves. I'm not add

Taylor C. Richberger 896 Aug 31, 2021
⛳ Simple, extensible, header-only C++17 argument parser released into the public domain.

⛳ flags Simple, extensible, header-only C++17 argument parser released into the public domain. why requirements api get get (with default value) posit

sailormoon 195 Apr 23, 2022
easy to use, powerful & expressive command line argument parsing for modern C++ / single header / usage & doc generation

clipp - command line interfaces for modern C++ Easy to use, powerful and expressive command line argument handling for C++11/14/17 contained in a sing

André Müller 872 May 11, 2022
Argh! A minimalist argument handler.

Frustration-free command line processing So many different command line processing libraries out there and none of them just work! Some bring their wh

Adi Shavit 984 May 12, 2022
udmp-parser: A Windows user minidump C++ parser library.

udmp-parser: A Windows user minidump C++ parser library. This is a cross-platform (Windows / Linux / OSX / x86 / x64) C++ library that parses Windows

Axel Souchet 86 May 1, 2022
A simple to use, composable, command line parser for C++ 11 and beyond

Clara v1.1.5 !! This repository is unmaintained. Go here for a fork that is somewhat maintained. !! A simple to use, composable, command line parser f

Catch Org 651 Apr 30, 2022
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.

CLI11: Command line parser for C++11 What's new • Documentation • API Reference CLI11 is a command line parser for C++11 and beyond that provides a ri

null 2.1k May 10, 2022
Lightweight C++ command line option parser

Release versions Note that master is generally a work in progress, and you probably want to use a tagged release version. Version 3 breaking changes I

null 3.1k May 12, 2022
A simple to use, composable, command line parser for C++ 11 and beyond

Lyra A simple to use, composing, header only, command line arguments parser for C++ 11 and beyond. Obtain License Standards Stats Tests License Distri

Build Frameworks Group 337 May 8, 2022
Tiny command-line parser for C / C++

tinyargs Another commandline argument parser for C / C++. This one is tiny, source only, and builds cleanly with -Wall -pedantic under C99 and C++11 o

Erik Agsjö 5 Nov 11, 2021
Elf and PE file parser

PelfParser PelfParser is a very simple C++ library for parsing Windows portable executable files and Executable and Linkable Format files, it only sup

Rebraws 1 Oct 29, 2021
A math parser made in 1 hour using copilot.

An entire math parser made with Copilot Copilot wrote 91% of the code in this, amazing isn't it? It supports all normal mathematical expressions excep

Duckie 4 Dec 7, 2021
A parser for InnoDB file formats

Introduction Inno_space is a parser for InnoDB file formats. It parse the .ibd file to human readable format. The origin idea come from Jeremy Cole's

Zongzhi Chen 70 May 12, 2022
An extremely fast FEC filing parser written in C

FastFEC A C program to stream and parse FEC filings, writing output to CSV. This project is in early stages but works on a wide variety of filings and

The Washington Post 48 May 18, 2022
JSONes - c++ json parser & writer. Simple api. Easy to use.

JSONes Just another small json parser and writer. It has no reflection or fancy specs. It is tested with examples at json.org Only standart library. N

Enes Kaya ÖCAL 2 Dec 28, 2021
A simple parser for the PBRT file format

PBRT-Parser (V1.1) The goal of this project is to provide a free (apache-lincensed) open source tool to easily (and quickly) load PBRT files (such as

Ingo Wald 191 Apr 10, 2022
A library for interactive command line interfaces in modern C++

cli A cross-platform header only C++14 library for interactive command line interfaces (Cisco style) Features Header only Cross-platform (linux and wi

Daniele Pallastrelli 782 May 9, 2022
Activity Indicators for Modern C++

Highlights Thread-safe progress bars and spinners Header-only library. Grab a copy of include/indicators. Single-header version in single_include/indi

Pranav 2k May 17, 2022
Table Maker for Modern C++

Source for the above image can be found here Table of Contents Quick Start Formatting Options Style Inheritance Model Word Wrapping Font Alignment Fon

Pranav 1.2k May 17, 2022