Basic use of argparse–parameter parsing

Article directory

    • argparse
    • start using
      • Positional parameters
      • Optional parameters
      • Use the flag() function
      • Required parameters
      • is_used(“–arg”)
      • Repeating and mutually exclusive optional parameters
      • Negative parameters
      • Combine positional and optional parameters
      • Print help
      • parameter list
      • Composite parameters
      • Numeric type conversion
      • Default -h and -v
      • Collect remaining parameters
      • parent-child parser
      • Parse unknown parameters
      • Custom prefix characters
      • Custom assigned characters

argparse

  • Project address argparse

Get started

Positional parameters

int positionalArguments(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe", "0.0.0");

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

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        return 1;
    }

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

    return 0;
}

Console commands and output

XHCompiler.exe 15
-------------------------------------------------- ---------------------------------------
225

Optional parameters

int optionalArguments(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe", "0.0.0");

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

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }

    if (program["--verbose"] == true) {<!-- -->
        std::cout << "Verbosity enabled" << std::endl;
    }
}

Console commands and output

XHCompiler.exe --verbose
-------------------------------------------------- ---------------------------------------
Verbosity enabled

Use flag() function

The flag function is the abbreviation of .default_value(false).implicit_value(true); like above.

int optionalArguments(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe", "0.0.0");

    program.add_argument("--verbose")
            .help("increase output verbosity")
            .flag();

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }

    if (program["--verbose"] == true) {<!-- -->
        std::cout << "Verbosity enabled" << std::endl;
    }
}

No duplicate results here

Required parameters

In some cases, we need some required parameters. We only need to add required() to the optional parameters to express it.

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

If we add a default value to this parameter, we can program non-essential parameters, but when actually used, the parameter exists in the form of a default value.

program.add_argument("-o", "--output")
  .default_value(std::string("-"))
  .required()
  .help("specify the output file.");
int requiredArguments(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe", "0.0.0");

    program.add_argument("--verbose")
            .help("increase output verbosity")
            .flag();

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


    program.add_argument("-b", "--base")
            .default_value(std::string("-"))
            .required()
            .help("specify the base.");

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }

    auto output = program.get<std::string>("--output");
    std::cout << output << std::endl;
    auto base = program.get<std::string>("--base");
    std::cout << base << std::endl;
    return 0;
}

Console commands and output

XHCompiler.exe -o "aaa" -b "bbb"
-------------------------------------------------- ---------------------------------------
aaa
bbb

is_used(“–arg”)

auto explicit_verbose = program.is_used("--verbose");
std::cout << explicit_verbose << std::endl;

Use is_used(“–arg”) to determine whether the optional parameter is used. You can add the following code to the above example. When outputting, it will output whether you used the verbose parameter.

Duplicate and mutually exclusive optional parameters

  • Repeated optional parameters
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::exception & amp; err) {<!-- -->
  std::cerr << err.what() << std::endl;
  std::cerr << program;
  std::exit(1);
}

auto colors = program.get<std::vector<std::string>>("--color"); // {"red", "green", "blue"}
int verbosity = 0;
program.add_argument("-V", "--verbose")
  .action([ & amp;](const auto & amp;) {<!-- --> + + verbosity; })
  .append()
  .default_value(false)
  .implicit_value(true)
  .nargs(0);

program.parse_args(argc, argv); // Example: ./main -VVVV

std::cout << "verbose level: " << verbosity << std::endl; // verbose level: 4
  • Mutually exclusive optional parameters
auto & amp;group = program.add_mutually_exclusive_group();
group.add_argument("--first");
group.add_argument("--second");

Negative parameters

int negativeNumbers(int argc, char *argv[])
{<!-- -->
    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::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }
    auto colors = program.get<std::vector<float>>("floats");
    auto inta = program.get<int>("integer");
    std::cout << colors[0] << std::endl;
    std::cout <<inta << std::endl;
    return 0;
}

Output results

XHCompiler.exe -2 -2.2 -3.3 -4.4 -5.5
--------------------------------------------------
-2.2
-2

Combine positional and optional parameters

int compareArgs(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program;

    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::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }

    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;
    }
    return 0;
}
E:\XHCompiler\cmake-build-debug>XHCompiler.exe 15
225

E:\XHCompiler\cmake-build-debug>XHCompiler.exe 15 --verbose
The square of 15 is 225

E:\XHCompiler\cmake-build-debug>XHCompiler.exe --verbose 15
The square of 15 is 225

Print help

int main(int argc, char *argv[]) {<!-- -->
    int ret = 0;
// ret = positionalArguments(argc, argv);
// ret = optionalArguments(argc, argv);
// ret = requiredArguments(argc, argv);
// ret = negativeNumbers(argc, argv);
// ret = compareArgs(argc, argv);


    argparse::ArgumentParser program("main");
    program.add_argument("thing").help("Thing to use.").metavar("THING");
    program.add_argument("--member").help("The alias for the member to pass to.").metavar("ALIAS");
    program.add_argument("--verbose").default_value(false).implicit_value(true);

    program.add_description("Forward a thing to the next member.");
    program.add_epilog("Possible things include betingalw, chiz, and res.");

    program.parse_args(argc, argv);

    std::cout << program << std::endl;

    return ret;
}

Output results

E:\XHCompiler\cmake-build-debug>XHCompiler.exe --help
Usage: main [--help] [--version] [--member ALIAS] [--verbose] THING

Forward a thing to the next member.

Positional arguments:
  THING Thing to use.

Optional arguments:
  -h, --help shows help message and exits
  -v, --version prints version information and exits
  --member ALIAS The alias for the member to pass to.
  --verbose

Possible things include betingalw, chiz, and res.

Parameter list

We can say to use nargs(int) to determine the number of parameters. You can also use scan(g’, float) to determine the input and type and subsequent composite parameters to determine

int paramsList(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program;

    program.add_argument("-i", "--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::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }

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

    for(auto file : files)
    {<!-- -->
        std::cout << file << " ";
    }
    std::cout << std::endl;
    return 0;
}

Output results

E:\XHCompiler\cmake-build-debug>XHCompiler.exe --input_files "aa aa" "bb"
aa aabb

E:\XHCompiler\cmake-build-debug>XHCompiler.exe --input_files aa bb
aabb

Composite parameters

int compoundParamsList(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program;

    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::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }

    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}
    std::cout << a << std::endl;
    std::cout << b << std::endl;
    for(auto file : c)
    {<!-- -->
        std::cout << file << " ";
    }
    std::cout << std::endl;
    return 0;
}

Output results

E:\XHCompiler\cmake-build-debug>XHCompiler.exe -abc 1.0 2.0
1
1
1 2

Numeric type conversion

We use ‘g’ when using the scan() function. These conversions have some predetermined types, as follows:

td>

Shape interpretation
‘a’ or ‘A’ Hexadecimal floating point number
‘e’ or ‘E’ Scientific notation (floating point number)
‘f’ or ‘F’ Fixed representation (floating point)
‘g’ or ‘G’ General form (fixed or scientific form)
‘d’ Decimal
‘i’ std::from_charsSyntax with base == 10
‘o’ Octal (unsigned)
‘u’ Decimal (unsigned)
‘x’ or ‘X’ Hexadecimal (unsigned)

Default -h and -v

For the default -h and -v if we need our own, we can use the following functions to disable the default parameters

int defaultCommand(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe", "1.0"
                                     , argparse::default_arguments::none);

    program.add_argument("-h", "--help")
            .action([=](const std::string & amp; s) {<!-- -->
                std::cout << program.help().str();
            })
            .default_value(false)
            .help("shows help message")
            .implicit_value(true)
            .nargs(0);
    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }
    return 0;
}

Collect remaining parameters

Just use the remaining() function in the parameters

int remainingParamsList(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program;

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

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

    program.add_argument("-m")
            .default_value(std::string("a.out"));

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        std::exit(1);
    }

    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 & amp; e) {<!-- -->
        std::cout << "No files provided" << std::endl;
    }
    return 0;
}

output

E:\XHCompiler\cmake-build-debug>XHCompiler.exe aa bb ccbb ddfb
4 files provided
aa
bb
ccbb
ddfb

Note: Remember to place all optional parameters before the remaining parameters. If an optional parameter is placed after the remaining parameters, it will also be treated as a remaining parameter:

Parent-child parser

int parentAndChildParamsList(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe");

    // git add subparser
    argparse::ArgumentParser add_command("add");
    add_command.add_description("Add file contents to the index");
    add_command.add_argument("files")
            .help("Files to add content from. Fileglobs (e.g. *.c) can be given to add all matching files.")
            .remaining();

    // git commit subparser
    argparse::ArgumentParser commit_command("commit");
    commit_command.add_description("Record changes to the repository");
    commit_command.add_argument("-a", "--all")
            .help("Tell the command to automatically stage files that have been modified and deleted.")
            .default_value(false)
            .implicit_value(true);

    commit_command.add_argument("-m", "--message")
            .help("Use the given <msg> as the commit message.");

    // git cat-file subparser
    argparse::ArgumentParser catfile_command("cat-file");
    catfile_command.add_description("Provide content or type and size information for repository objects");
    catfile_command.add_argument("-t")
            .help("Instead of the content, show the object type identified by <object>.");

    catfile_command.add_argument("-p")
            .help("Pretty-print the contents of <object> based on its type.");

    // git submodule subparser
    argparse::ArgumentParser submodule_command("submodule");
    submodule_command.add_description("Initialize, update or inspect submodules");
    argparse::ArgumentParser submodule_update_command("update");
    submodule_update_command.add_description("Update the registered submodules to match what the superproject expects");
    submodule_update_command.add_argument("--init")
            .default_value(false)
            .implicit_value(true);
    submodule_update_command.add_argument("--recursive")
            .default_value(false)
            .implicit_value(true);
    submodule_command.add_subparser(submodule_update_command);

    program.add_subparser(add_command);
    program.add_subparser(commit_command);
    program.add_subparser(catfile_command);
    program.add_subparser(submodule_command);

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        return 1;
    }
    return 0;
}

Try the execution output yourself.

  • The parser has is_subcommand_used() which checks if a subcommand is used
  • We can also obtain subparsers using the following function
argparse::ArgumentParser program("test");

program.add_argument("--dir");
program.at("--dir").default_value(std::string("/home/user"));

program.add_subparser(argparse::ArgumentParser{<!-- -->"walk"});
program.at<argparse::ArgumentParser>("walk").add_argument("depth");

Parse unknown parameters

int unknownParamsList(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe");
    program.add_argument("--foo").implicit_value(true).default_value(false);
    program.add_argument("bar");

    auto unknown_args =
            program.parse_known_args({<!-- -->"test", "--foo", "--badger", "BAR", "spam"});

    assert(program.get<bool>("--foo") == true);
    assert(program.get<std::string>("bar") == std::string{<!-- -->"BAR"});
    assert((unknown_args == std::vector<std::string>{<!-- -->"--badger", "spam"}));
    return 0;
}

Custom prefix characters

int selfPrefixChar(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe");
    program.set_prefix_chars("- + /");

    program.add_argument(" + f");
    program.add_argument("--bar");
    program.add_argument("/foo");

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        return 1;
    }

    if (program.is_used(" + f")) {<!-- -->
        std::cout << " + f : " << program.get(" + f") << "\\
";
    }

    if (program.is_used("--bar")) {<!-- -->
        std::cout << "--bar : " << program.get("--bar") << "\\
";
    }

    if (program.is_used("/foo")) {<!-- -->
        std::cout << "/foo : " << program.get("/foo") << "\\
";
    }

    return 0;
}

Custom assigned characters

int selfAssignmentChar(int argc, char *argv[])
{<!-- -->
    argparse::ArgumentParser program("XHCompiler.exe");
    program.set_prefix_chars("- + /");
    program.set_assign_chars("=:");

    program.add_argument("--foo");
    program.add_argument("/B");

    try {<!-- -->
        program.parse_args(argc, argv);
    }
    catch (const std::exception & amp; err) {<!-- -->
        std::cerr << err.what() << std::endl;
        std::cerr << program;
        return 1;
    }

    if (program.is_used("--foo")) {<!-- -->
        std::cout << "--foo : " << program.get("--foo") << "\\
";
    }

    if (program.is_used("/B")) {<!-- -->
        std::cout << "/B : " << program.get("/B") << "\\
";
    }
    return 0;
}

output

E:\XHCompiler\cmake-build-debug>XHCompiler.exe /B a
/B : a

E:\XHCompiler\cmake-build-debug>XHCompiler.exe /B=a
/B :a

E:\XHCompiler\cmake-build-debug>XHCompiler.exe /B:a
/B :a