-
Notifications
You must be signed in to change notification settings - Fork 0
Examples
Practical, copy-pasteable examples for FancyArgumentParser. Every example
compiles as-is against the single header (#include "ArgParse/argparse.h") and
works from C++11 onward. Where C++20 lets you write the same thing more
nicely (designated-initializer "keyword" syntax), it is tucked into a
collapsible C++20 keyword style block right under the baseline version — so
both are always available without doubling the page.
Looking for features that only exist in C++20 (the
import argparse;module,std::rangespost-processing, the keyword-spec deep dive)? See the C++20 features page.
- Hello world
- Named arguments and auto-generated help
- Shorter ways to declare an argument
- Positional arguments
- Mixing positional and named arguments
- How many values: nargs
- Choices and validators
- Flags and default values
- Boolean arguments
- Reading results by name
- Variable binding with BindTo
- Configuring the parser with ParserSpec
- Variable argument count, custom prefix and ignore-unknown
- Using a custom namespace
- Overriding the usage line
- Error handling patterns
- Real-world example: temperature converter
- Simple Polish-notation calculator
The smallest useful program: one required named argument, then read it back.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("greet").SetDescription("A tiny greeter");
parser.AddArgument(argparse::CreateNamedArgument("n", "name", 1,
argparse::ArgTypeCast::e_String, true).SetHelp("Who to greet"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
std::cout << "Hello, " << obj.GetAsString("name") << "!" << std::endl;
return 0;
}>>> greet --name World
Hello, World!
>>> greet
Required argument with name "name" does not exist
...auto-generated help...
C++20 keyword style
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "n", .longName = "name",
.nargs = 1, .type = argparse::ArgTypeCast::e_String,
.required = true, .help = "Who to greet"}));A fuller version with two integer options that each take one or more values.
The -h/--help option and the whole help text are generated for you.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("Program name").SetDescription("Description of program");
parser.AddArgument(argparse::CreateNamedArgument()
.SetLongName("numbers")
.SetAnyNumberOfArgumentsButAtLeastOne()
.SetType(argparse::ArgTypeCast::e_int));
parser.AddArgument(argparse::CreateNamedArgument()
.SetLongName("some_boring_long_name")
.SetAnyNumberOfArgumentsButAtLeastOne()
.SetType(argparse::ArgTypeCast::e_int)
.SetHelp("some_boring_long_name description with some important information for user.")
.SetRequired(false));
auto obj = parser.ParseArgs(argc, argv);
if (obj.IsArgValid())
{
auto arg = obj.GetArg("numbers");
if (arg.GetArgumentExists())
for (auto& el : arg.GetAsVecInt())
std::cout << el << std::endl;
}
else
{
std::cout << obj.GetErrorString() << std::endl;
std::cout << parser.GetHelp(80) << std::endl;
}
return 0;
}This creates the keys -n/--numbers, -s/--some_boring_long_name and
-h/--help. Typical output without any arguments:
main.cpp [-n,--numbers [n ...] ] [-s,--some_boring_long_name [s ...] ] -h,--help
Description of program
optional arguments:
-n,--numbers some numbers description with some important information
for user. Type: INT. Args count: at least one.
-s,--some_boring_long_name
some_boring_long_name description with some important information
for user. Type: INT. Args count: at least one.
-h,--help Show help!
Note: a terminal cannot really pass an infinite number of arguments — most shells cap the whole command line at around 8 kB.
The fluent setters above are one of three equivalent styles. You can also pass everything positionally to the factory, or (in C++20) name the fields.
// Positional factory arguments (C++11+):
parser.AddArgument(argparse::CreateNamedArgument("n", "numbers",
argparse::kFromOneToInfiniteArgCount, argparse::ArgTypeCast::e_int, false,
"some numbers description with some important information for user."));C++20 keyword style
parser.AddArgument(argparse::CreateNamedArgument({
.longName = "numbers",
.nargs = argparse::kFromOneToInfiniteArgCount,
.type = argparse::ArgTypeCast::e_int,
.required = false,
.help = "some numbers description with some important information for user."}));Short names may be written as a single char ('n') as well as a string ("n").
A positional argument is passed as a raw value without any key — that is the only difference from a named argument.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("main").SetDescription("ArgParse example");
parser.AddArgument(argparse::CreatePositionalArgument("int1")
.SetType(argparse::ArgTypeCast::e_int).SetRequired(false));
auto obj = parser.ParseArgs(argc, argv);
if (obj.IsArgValid() && obj.GetArg("int1").GetArgumentExists())
std::cout << "int1 = " << obj.GetAsInt("int1") << std::endl;
else if (!obj.IsArgValid())
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 0;
}C++20 keyword style
parser.AddArgument(argparse::CreatePositionalArgument({
.name = "int1", .type = argparse::ArgTypeCast::e_int, .required = false}));Positional and named arguments can be combined freely and passed in any order.
Below, a small cp-like tool takes two positionals (source, dest) and one
named flag (-f/--force).
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("mycp").SetDescription("Copy SOURCE to DEST");
parser.AddArgument(argparse::CreatePositionalArgument("source").SetHelp("File to copy from"));
parser.AddArgument(argparse::CreatePositionalArgument("dest").SetHelp("File to copy to"));
parser.AddArgument(argparse::CreateNamedArgument("f", "force").SetArgumentIsFlag()
.SetRequired(false).SetHelp("Overwrite destination if it exists"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
const std::string src = obj.GetAsString("source"); // by value: always safe
const std::string dst = obj.GetAsString("dest");
const bool force = obj.GetArg("force").GetArgumentExists();
std::cout << "copy " << src << " -> " << dst << (force ? " (force)" : "") << std::endl;
return 0;
}>>> mycp a.txt --force b.txt
copy a.txt -> b.txt (force)
C++20 keyword style
parser.AddArgument(argparse::CreatePositionalArgument({.name = "source", .help = "File to copy from"}));
parser.AddArgument(argparse::CreatePositionalArgument({.name = "dest", .help = "File to copy to"}));
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "f", .longName = "force",
.nargs = 0, .required = false, .help = "Overwrite destination if it exists"}));nargs controls how many values an argument consumes. You can pass an exact
integer, one of the named constants, or a Python-style character.
| You want | Character | Constant |
|---|---|---|
| Exactly N |
N (an int) |
— |
| Zero or one | '?' |
kZeroOrOneArgCount |
| Zero or more | '*' |
kAnyArgCount |
| One or more | '+' |
kFromOneToInfiniteArgCount |
// '?' zero-or-one (falls back to the default when omitted)
parser.AddArgument(argparse::CreateNamedArgument("c", "count", '?')
.SetType(argparse::ArgTypeCast::e_int).SetDefault(1));
// '+' one-or-more values
parser.AddArgument(argparse::CreatePositionalArgument("files", '+'));
// '*' zero-or-more values
parser.AddArgument(argparse::CreateNamedArgument("x", "extra", '*'));C++20 keyword style
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "c", .longName = "count", .nargs = '?',
.type = argparse::ArgTypeCast::e_int}).SetDefault(1));
parser.AddArgument(argparse::CreatePositionalArgument({.name = "files", .nargs = '+'}));Restrict what values an argument accepts. SetChoices limits it to a fixed set;
the validators check each value with a rule and fail the parse (with a message)
when it doesn't hold.
#include <iostream>
#include <string>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("srv").SetDescription("Validators demo");
// Fixed set of allowed values.
parser.AddArgument(argparse::CreateNamedArgument("o", "op").SetRequired(false)
.SetChoices({"+", "-", "*", "/"}));
// Numeric range (also sets the type for you).
parser.AddArgument(argparse::CreateNamedArgument("p", "port").SetRequired(false)
.SetRange(1, 65535));
// Strictly positive.
parser.AddArgument(argparse::CreateNamedArgument("r", "ratio").SetRequired(false)
.SetType(argparse::ArgTypeCast::e_double).SetPositive());
// Any custom predicate, with an optional error message.
parser.AddArgument(argparse::CreateNamedArgument("m", "mode").SetRequired(false)
.SetValidator([](const std::string& v){ return v == "fast" || v == "safe"; },
"mode must be 'fast' or 'safe'"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << "error: " << obj.GetErrorString() << std::endl;
return 1;
}
std::cout << "ok" << std::endl;
return 0;
}>>> srv --port 70000
error: value out of range [1, 65535]
>>> srv --mode turbo
error: mode must be 'fast' or 'safe'
Built-in validators: SetRange (int / long long / double, as (lo, hi) or
(max)), SetPositive, SetNonNegative, SetPattern (regex), and the
filesystem checks SetExistingFile, SetExistingDirectory, SetExistingPath,
SetNonexistentPath (available when <filesystem> is, i.e. C++17+).
A predicate and its message can also be passed straight to the factory or a spec:
// Positional factory: ...help, predicate, message
parser.AddArgument(argparse::CreatePositionalArgument("name", 1,
argparse::ArgTypeCast::e_String, true, "a name",
[](const std::string& s){ return s == "ok"; }, "name must be 'ok'"));C++20 keyword style
SetChoices is not a spec field, so chain it afterwards (use an explicit
std::vector<std::string> to disambiguate the overload). The validator, however,
is a spec field:
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "o", .longName = "op", .required = false})
.SetChoices(std::vector<std::string>{"+", "-", "*", "/"}));
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "m", .longName = "mode", .required = false,
.validator = [](const std::string& v){ return v == "fast" || v == "safe"; },
.validator_message = "mode must be 'fast' or 'safe'"}));A flag holds no value — it is either present or not — created with
SetArgumentIsFlag() (equivalently nargs = 0). Every argument is required by
default, so an optional flag must opt out with SetRequired(false).
SetDefault(...) gives an optional argument a value to fall back on.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("build").SetDescription("Flags and default values");
parser.AddArgument(argparse::CreateNamedArgument("v", "verbose").SetArgumentIsFlag()
.SetRequired(false).SetHelp("Enable verbose output"));
parser.AddArgument(argparse::CreateNamedArgument("j", "jobs", 1, argparse::ArgTypeCast::e_int, false)
.SetDefault(1).SetHelp("Number of parallel jobs (default: 1)"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
const bool verbose = obj.GetArg("verbose").GetArgumentExists();
const int jobs = obj.GetAsInt("jobs"); // always present thanks to SetDefault
std::cout << "verbose = " << (verbose ? "true" : "false") << "\n";
std::cout << "jobs = " << jobs << std::endl;
return 0;
}>>> build -v
verbose = true
jobs = 1
>>> build
verbose = false
jobs = 1
C++20 keyword style
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "v", .longName = "verbose",
.nargs = 0, .required = false, .help = "Enable verbose output"}));
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "j", .longName = "jobs",
.type = argparse::ArgTypeCast::e_int, .required = false,
.help = "Number of parallel jobs (default: 1)"}).SetDefault(1));Use ArgTypeCast::e_bool for arguments whose value is a boolean. The accepted
spellings are true/True/TRUE and false/False/FALSE. (For a valueless
on/off switch, use SetArgumentIsFlag() instead — see Flags above.)
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("feature").SetDescription("Bool-typed arguments");
parser.AddArgument(argparse::CreateNamedArgument("d", "debug", 1,
argparse::ArgTypeCast::e_bool, false).SetDefault(false)
.SetHelp("Enable debug mode (true/false)"));
// A bool argument can also take several values.
parser.AddArgument(argparse::CreateNamedArgument("s", "switches",
argparse::kFromOneToInfiniteArgCount, argparse::ArgTypeCast::e_bool, false)
.SetHelp("A series of on/off switches"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
std::cout << "debug = " << (obj.GetAsBool("debug") ? "true" : "false") << "\n";
auto sw = obj.GetArg("switches");
if (sw.GetArgumentExists())
{
std::cout << "switches =";
for (bool b : sw.GetAsVecBool())
std::cout << ' ' << (b ? "on" : "off");
std::cout << std::endl;
}
return 0;
}>>> feature --debug true --switches true false TRUE
debug = true
switches = on off on
C++20 keyword style
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "d", .longName = "debug",
.nargs = 1, .type = argparse::ArgTypeCast::e_bool, .required = false,
.help = "Enable debug mode (true/false)"}).SetDefault(false));
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "s", .longName = "switches",
.nargs = argparse::kFromOneToInfiniteArgCount, .type = argparse::ArgTypeCast::e_bool,
.required = false, .help = "A series of on/off switches"}));After a successful parse you can read values straight off the result by argument
name, without going through GetArg(...) first:
int n = obj.GetAsInt("count");
double ratio = obj.GetAsDouble("ratio");
std::string name = obj.GetAsString("name");
std::vector<int> nums = obj.GetAsVecInt("numbers");These by-name getters return by value (a copy), so — unlike
GetArg(name).GetAsString() which returns a reference into the parsed object —
there is no lifetime dance: you can call them inline and store the result
however you like. Reach for the longer GetArg("count") form when you need the
Argument itself, e.g. to check GetArgumentExists() on an optional argument.
// safe, self-contained:
const std::string host = obj.GetAsString("host");
// also fine, but note GetAsString() here returns a reference into 'a':
auto a = obj.GetArg("host");
const std::string& hostRef = a.GetAsString();Instead of reading each value back, you can bind an argument to one of your
variables with BindTo(&var). After a successful parse the value lands there
automatically. BindTo also infers the argument's type from the variable, so no
SetType call is needed. Available from C++11.
#include <iostream>
#include <string>
#include <vector>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
// Bound variables. Initial values act as defaults for optional arguments.
std::string host = "localhost";
int port = 8080;
bool verbose = false;
std::vector<int> workers;
auto parser = argparse::ArgumentParser("serve").SetDescription("Start a server");
parser.AddArgument(argparse::CreateNamedArgument("H", "host", 1)
.SetRequired(false).SetHelp("Bind address").BindTo(&host));
parser.AddArgument(argparse::CreateNamedArgument("p", "port", 1)
.SetRequired(false).SetHelp("Port to listen on").BindTo(&port));
parser.AddArgument(argparse::CreateNamedArgument("v", "verbose", 1)
.SetRequired(false).SetHelp("Verbose logging").BindTo(&verbose));
parser.AddArgument(argparse::CreateNamedArgument("w", "workers")
.SetRequired(false).SetAnyNumberOfArgumentsButAtLeastOne()
.SetHelp("Worker ids").BindTo(&workers));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
// No GetArg(...).GetAsX() anywhere — the variables are already populated.
std::cout << "host=" << host << " port=" << port
<< " verbose=" << std::boolalpha << verbose << "\n";
std::cout << "workers (" << workers.size() << "):";
for (int id : workers) std::cout << " " << id;
std::cout << "\n";
return 0;
}>>> serve --host 0.0.0.0 --port 9090 --verbose true --workers 1 2 3
host=0.0.0.0 port=9090 verbose=true
workers (3): 1 2 3
>>> serve
host=localhost port=8080 verbose=false
workers (0):
Note: the bound variable must outlive the
ParseArgscall. If an optional argument is absent (and has no default), its bound variable is left untouched — so its initializer acts as the default. Bindings are applied only on a successful parse.
The parser-level options (description, prefixChars, addHelp,
allowAbbrev, ignoreUnknownArgs, …) can be set in one place by constructing
the parser from a ParserSpec, instead of chaining setters.
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
argparse::ParserSpec spec;
spec.name = "cptool";
spec.description = "Copy files";
spec.allowAbbrev = false;
auto parser = argparse::ArgumentParser(spec);
// ... AddArgument(...) as usual ...
auto obj = parser.ParseArgs(argc, argv);
return obj.IsArgValid() ? 0 : 1;
}C++20 keyword style
With designated initializers this reads like Python's ArgumentParser(...):
auto parser = argparse::ArgumentParser(argparse::ParserSpec{
.name = "cptool",
.description = "Copy files",
.allowAbbrev = false});kAnyArgCount accepts zero or more values (use kFromOneToInfiniteArgCount to
demand at least one). SetPrefixChars('+') changes the option prefix, and
SetIgnoreUnknownArgs(true) lets the parser skip options it doesn't recognise
instead of failing.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("sum")
.SetDescription("Sum any amount of numbers")
.SetPrefixChars('+')
.SetIgnoreUnknownArgs(true);
parser.AddArgument(argparse::CreateNamedArgument("n", "nums", argparse::kAnyArgCount,
argparse::ArgTypeCast::e_int, false).SetHelp("Numbers to add"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
long long total = 0;
auto arg = obj.GetArg("nums");
if (arg.GetArgumentExists())
for (int n : arg.GetAsVecInt())
total += n;
std::cout << "sum = " << total << std::endl;
return 0;
}>>> sum ++nums 3 4 5 ++junk hello
sum = 12
C++20 keyword style
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "n", .longName = "nums",
.nargs = argparse::kAnyArgCount, .type = argparse::ArgTypeCast::e_int,
.required = false, .help = "Numbers to add"}));If the default argparse namespace clashes with something in your project,
define ARGPARSE_NAMESPACE_NAME before including the header to rename it.
#define ARGPARSE_NAMESPACE_NAME cli
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = cli::ArgumentParser("greet").SetDescription("Custom namespace demo");
parser.AddArgument(cli::CreateNamedArgument("n", "name", 1, cli::ArgTypeCast::e_String, true)
.SetHelp("Who to greet"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
std::cout << "Hello, " << obj.GetAsString("name") << "!" << std::endl;
return 0;
}>>> greet --name World
Hello, World!
By default the usage line is generated from your arguments. SetUsage(...)
replaces just that first line with your own wording; the argument listings below
it are still generated for you.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("serve").SetDescription("Start a web server");
parser.SetUsage("serve --port PORT [--host HOST]");
parser.AddArgument(argparse::CreateNamedArgument("p", "port", 1,
argparse::ArgTypeCast::e_int, true).SetHelp("Port to listen on"));
parser.AddArgument(argparse::CreateNamedArgument("H", "host", 1,
argparse::ArgTypeCast::e_String, false).SetDefault(std::string("0.0.0.0"))
.SetHelp("Interface to bind"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
std::cout << "listening on " << obj.GetAsString("host")
<< ":" << obj.GetAsInt("port") << std::endl;
return 0;
}>>> serve --port 8080
listening on 0.0.0.0:8080
>>> serve
Required argument with name "port" does not exist
serve --port PORT [--host HOST]
Start a web server
...
Tip: put positional arguments before named ones on the command line. The parser collects positionals first, so
mytool FILE --flagworks whilemytool --flag FILEmay misassignFILE.
There are two distinct kinds of errors:
-
Setup errors — a malformed definition (e.g. an argument with neither a
name nor a positional name).
AddArgumentthrows for these; they are programmer mistakes. You only need atry/catchif you build arguments dynamically from external data. -
Input errors — bad user input (missing required argument, wrong type,
value out of choices, failed validator). These are never thrown; they are
reported through the result via
IsArgValid()andGetErrorString().
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
argparse::ArgumentParser parser("app");
try
{
parser.AddArgument(argparse::CreateNamedArgument("p", "port", 1,
argparse::ArgTypeCast::e_int, true).SetHelp("Port to listen on"));
}
catch (const std::exception& e)
{
std::cerr << "argument setup error: " << e.what() << std::endl;
return 2;
}
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cerr << "error: " << obj.GetErrorString() << "\n\n";
std::cerr << parser.GetHelp(80) << std::endl;
return 1;
}
std::cout << "listening on port " << obj.GetAsInt("port") << std::endl;
return 0;
}>>> app -p 8080
listening on port 8080
>>> app
error: Required argument with name "port" does not exist
...
Combines a typed positional (double), two required named arguments constrained
with SetChoices, and an epilogue.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("convert").SetDescription("Convert a temperature between units");
parser.AddArgument(argparse::CreatePositionalArgument("value")
.SetType(argparse::ArgTypeCast::e_double).SetHelp("Temperature value"));
parser.AddArgument(argparse::CreateNamedArgument("f", "from").SetRequired(true)
.SetChoices({"C", "F", "K"}).SetHelp("Source unit"));
parser.AddArgument(argparse::CreateNamedArgument("t", "to").SetRequired(true)
.SetChoices({"C", "F", "K"}).SetHelp("Target unit"));
parser.SetEpilogue("Units: C = Celsius, F = Fahrenheit, K = Kelvin");
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
const double v = obj.GetAsDouble("value");
const std::string from = obj.GetAsString("from");
const std::string to = obj.GetAsString("to");
double c = from == "C" ? v : from == "F" ? (v - 32.0) * 5.0 / 9.0 : v - 273.15;
double out = to == "C" ? c : to == "F" ? c * 9.0 / 5.0 + 32.0 : c + 273.15;
std::cout << v << from << " = " << out << to << std::endl;
return 0;
}>>> convert 100 -f C -t F
100C = 212F
>>> convert 100 -f C -t Q
Value 'Q' is out of choices for "to"
C++20 keyword style
SetChoices is chained since it is not a spec field:
parser.AddArgument(argparse::CreatePositionalArgument({
.name = "value", .type = argparse::ArgTypeCast::e_double, .help = "Temperature value"}));
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "f", .longName = "from", .required = true, .help = "Source unit"})
.SetChoices(std::vector<std::string>{"C", "F", "K"}));
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "t", .longName = "to", .required = true, .help = "Target unit"})
.SetChoices(std::vector<std::string>{"C", "F", "K"}));Two positional numbers and an operator constrained by SetChoices.
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("calc").SetDescription("Polish-notation calc");
parser.AddArgument(argparse::CreatePositionalArgument("nums")
.SetType(argparse::ArgTypeCast::e_double).SetNumberOfArguments(2));
parser.AddArgument(argparse::CreateNamedArgument("o", "operation")
.SetRequired(true).SetChoices({"+", "-", "*", "/"}));
parser.SetEpilogue("This is an example of an epilogue, placed at the end of the help.");
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
const std::vector<double> nums = obj.GetAsVecDouble("nums");
const std::string op = obj.GetAsString("operation");
std::cout << nums[0] << op << nums[1] << "=";
if (op == "+") std::cout << nums[0] + nums[1] << std::endl;
else if (op == "-") std::cout << nums[0] - nums[1] << std::endl;
else if (op == "*") std::cout << nums[0] * nums[1] << std::endl;
else std::cout << nums[0] / nums[1] << std::endl;
return 0;
}>>> calc 12121 222 -o +
12121+222=12343
>>> calc 12121 222 -o %
Value '%' is out of choices for "operation"
C++20 keyword style
parser.AddArgument(argparse::CreatePositionalArgument({
.name = "nums", .nargs = 2, .type = argparse::ArgTypeCast::e_double}));
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "o", .longName = "operation", .required = true})
.SetChoices(std::vector<std::string>{"+", "-", "*", "/"}));Want the C++20-only capabilities — consuming the library as a module, keyword
specs in depth, or piping parsed values through std::ranges? Continue to
C++20 features.