Argh! 一个极简的参数处理器。
Frustration-free command line processing So many different command line processing libraries out there and none of them just work!
Some bring their whole extended family of related and unrelated external dependencies (yes, I'm looking at you Boost).
Some require quirky syntax and/or very verbose setups that sacrifice simplicity for the generation of a cute usage message and validation. Many come to dominate yourmain()file and yet others do not build on multiple plaforms - for some even their own tests and trivial usage cause crashes on some systems. Argh!
If you're writing a highly-sophisticated command line tool, then Boost.Program_options and its kind might give you many advanced options. However, if you need to get up and running quickly, effectively and with minimal fuss, give the single header-file argh a try.
It doesn't get much simpler than this:
#include <iostream>
#include "argh.h"
int main(int, char* argv[])
{
argh::parser cmdl(argv);
if (cmdl[{ "-v", "--verbose" }])
std::cout << "Verbose, I am.\n";
return EXIT_SUCCESS;
}Contrary to many alternatives, argh takes a minimalist laissez-faire approach, very suitable for fuss-less prototyping with the following rules:
The API is:
[] and () operatorsfor too)std::istream >>) on the user side after the parsing phasestd dependenciesargh does not care about:
-' preceded your optionCreate parser:
auto cmdl = argh::parser(argc, argv);In fact, you can even drop argc. This will also work:
argh::parser cmdl(argv);Positional argument access by (integer) index with [<size_t>]:
cout << "Exe name is: " << cmdl[0] << '\n';
^^^
assert(cmdl[10000].empty()); // out-of-bound index returns empty string
^^^^^Boolean flag argument access by (string) name with [<std::string>]:
cout << "Verbose mode is " << ( cmdl["verbose"] ? "ON" : "OFF" ) << '\n';
^^^^^^^^^^^Any dashes are trimmed so are not required.
Your flag can have several alternatives, just list them with [{ "<name-1>", "<name-2>", ... }]:
cout << "Verbose mode is " << ( cmdl[{ "-v", "--verbose" }] ? "ON" : "OFF" ) << '\n';
^^^^^^^^^^^^^^^^^^^^^^^Beyond bool and std::string access with [], as shown above, we can also access the argument values as an std::istream. This is very useful for type conversions.
std::istream positional argument access by (integer) index with (<size_t>):
std::string my_app_name;
cmdl(0) >> my_app_name; // streaming into a string
^^^
cout << "Exe name is: " << my_app_name << '\n';We can also check if a particular positional arg was given or not (this is like using [<std::string>] above):
if (!cmdl(10))
cerr << "Must provide at least 10 arguments!" << '\n';
else if (cmdl(11))
cout << "11th argument is: " << cmdl[11] << '\n';But we can also set default values for positional arguments. These are passed as the second argument:
float scale_factor;
cmdl(2, 1.0f) >> scale_factor;
^^^^^^^If the position argument was not given or the streaming conversion failed, the default value will be used.
Similarly, parameters can be accessed by name(s) (i.e. by string or list of string literals) with:(<std::string> [, <default value>]) or ({ "<name-1>", "<name-2>", ... } [, <default value>]):
float scale_factor;
cmdl("scale", 1.0f) >> scale_factor; // Use 1.0f as default value
^^^^^^^^^^^^^
float threshold;
if (!(cmdl({ "-t", "--threshold"}) >> threshold)) // Check for missing param and/or bad (inconvertible) param value
cerr << "Must provide a valid threshold value! Got '" << cmdl("threshold").str() << "'" << endl;
else ^^^^^^
cout << "Threshold set to: " << threshold << '\n';As shown above, use std::istream::str() to get the param value as a std:string or just stream the value into a variable of a suitable type. Standard stream state indicates failure, including when the argument was not given.
When using multiple names, the first value found will be returned.
Positional arguments can be iterated upon directly using range-for:
cout << "Positional args:\n";
for (auto& pos_arg : cmdl)
cout << '\t' << pos_arg << '\n';Similarly, cmdl.size() will return the count of positional arguments.
Positional arguments, flags and parameters are accessible as "ranges":
cout << "Positional args:\n";
for (auto& pos_arg : cmdl.pos_args())
cout << '\t' << pos_arg << '\n';
cout << "\nFlags:\n";
for (auto& flag : cmdl.flags())
cout << '\t' << flag << '\n';
cout << "\nParameters:\n";
for (auto& param : cmdl.params())
cout << '\t' << param.first << " : " << param.second << '\n';If a parameter appears several times in the command line, all its duplicates may be accessed, in order, like so:
cout << "\nValues for all `--input` parameters:\n";
for (auto& param : cmdl.params("input")) // iterate on all params called "input"
cout << '\t' << param.first << " : " << param.second << '\n';By default, options are assumed to be boolean flags. When this is not what you want, there are several ways to specify when an option is a parameter with an associated value.
Specify PREFER_PARAM_FOR_UNREG_OPTION mode to interpret any <option> <non-option> as <parameter-name> <parameter-value>:
using namespace argh;
auto cmdl = parser(argc, argv, parser::PREFER_PARAM_FOR_UNREG_OPTION);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cout << cmdl("--threshold").str() << '\n';Pre-register an expected parameter name with add_param() (before calling parse()):
argh::parser cmdl;
cmdl.add_param("threshold"); // pre-register "threshold" as a param: name + value
cmdl.parse(argc, argv);
cout << cmdl("threshold").str() << '\n';You may also batch pre-register multiple options as parameters with add_params({ ... }):
argh::parser cmdl;
cmdl.add_params({ "-t", "--threshold", "-s", "--scale" }); // batch pre-register multiple params: name + value
cmdl.parse(argc, argv);
cout << cmdl("threshold").str() << '\n';Unregistered options will default to boolean flags.
Since pre-registration has to be done before parsing, we might as well just use the ctor:
argh::parser cmdl({ "-t", "--threshold", "-s", "--scale" }); // batch pre-register multiple params: name + value
cmdl.parse(argc, argv);
cout << cmdl("threshold").str() << '\n';Use a = with no spaces around it within the option when calling the app:
>> my_app --threshold=42
42This will automatically be interpreted as a named parameter-value pair.
--<name>=<value> (with no spaces, one or more dashes), e.g. --answer=42, will be parsed as <parameter-name> <parameter-value>.
To disable this specify the NO_SPLIT_ON_EQUALSIGN mode.SINGLE_DASH_IS_MULTIFLAG mode, a.k.a Compound Arguments, will split a single-hyphen argument into multiple single-character flags (as is common in various POSIX tools).SINGLE_DASH_IS_MULTIFLAG, you can still pre-register the last character as a param with the value, such that if we pre-register f as a param, >> myapp -xvf 42 will be parsed with two boolean flags x and v and a one param f=42.--config="C:\Folder\With Space\Config.ini"), prefer using .str() instead of >> to avoid the default automatic whitespace input stream tokenization:cout << cmdl({ "-c", "--config" }).str().Any command line is composed of 2 types of Args:
config.json - (and that are not negative numbers).-v, --verbose --gamma 2.2Thus, any command line can always be broken into some combination of (1) positional args (2) flags and (3) parameters.
Parse the command line using either
parse() method: parser::parse([argc,] argv [, mode]); orargh::parser([argc,] argv [, mode]);argc, so in default mode just use:parser(argv);Extra flexibility can be added by specifying parsing modes:
NO_SPLIT_ON_EQUALSIGN:
By default, an option of the form --pi=22/7 will be parsed as a parameter pi with an associated value "22/7".
By setting this mode, it will be not be broken at the =.PREFER_FLAG_FOR_UNREG_OPTION:
Split <option> <non-option> into <flag> and <pos_arg>.
e.g. myapp -v config.json will have v as a lit flag and config.json as a positional arg.
This is the default mode.PREFER_PARAM_FOR_UNREG_OPTION:
Interpret <option> <non-option> as <parameter-name> <parameter-value>.
e.g. myapp --gamma 2.2 will have gamma as a parameter with the value "2.2".SINGLE_DASH_IS_MULTIFLAG:
Splits an option with a single dash into separate boolean flags, one for each letter (a.k.a Compound Arguments).
e.g. in this mode, -xvf will be parsed as 3 separate flags: x, v, f.Use bracket operators to access flags and positional args:
operator[index] to access position arg strings by index:assert(cmdl[0] == argv[0]), the app name.operator[string] to access boolean flags by name:if (cmdl["v"]) make_verbose();operator[{...}] to access boolean flags by multiple names:if (cmdl[{ "v", "verbose" }]) make_verbose();Use the parenthesis operators to get an std::istream to stream values from parameters and positional args:
operator(index) to access position arg istream by index:cmdl(0) >> my_app_name.operator(string) to access parameter values by name:cmdl("scale") >> scale_factor;operator({...}) to access parameter values by multiple names:cmdl({ "-s", "--scale" }) >> scale_factor;operator(index, <default>) and operator(string/{list}, <default>) to stream a default value if the arg did not appear on the command line:cmdl("scale", 1.0f) >> scale_factor;The streaming happens at the user's side, so conversion failure can be checked there: e.g
if暂无开放 Issues,或尚未同步最近议题。