-
Notifications
You must be signed in to change notification settings - Fork 0
Defining Options
Defining options is conceptually very simple. You need to create and configure an Option
(or WOption
) object and add it to a parser via its add()
method. An option definition consists of:
-
A set of names such as:
"-c", "--cd", "--directory"
. The names are the only required part of an option, so they must be passed in the constructor. Order of names doesn't matter except for which one is the first one. The first name (also called the main name) is used as the identifier of the option when it needs to be referred to in other contexts. It is also the one displayed in the usage string (produced byformatUsage
). All names are shown in help (produced byformatHelp
). Names must start with one of the option prefixes configured for the parser. -
A handler, which is a function object (often a lambda) that is invoked when the option is encountered during parsing out. A handler is not strictly required - if you don't provide one a default handler simply does nothing. Since doing nothing for an option is rare (but happens) usually you do need to provide one. The handler also indicates whether the option accepts an argument and, if so, whether the argument is optional. It works like this:
- If the handler doesn't have arguments then the option doesn't have an argument.
- If the handler has an argument that is convertible from
std::string_view
(orstd::wstring_view
forWOption
) then the option has a required argument. Its value will be passed as the argument to the handler. - If the handler has an argument that is convertible from
std::optional<std::string_view>
(substitute "w" as usual for wide options) then the option has an optional argument. Its value orstd::nullopt
will be passed as the argument to the handler.
Pretty straightforward isn't it?
-
A help string. This is the description of the option produced when generating help via
formatHelp
method. Not required but obviously highly desired if you use Argum's generated help. -
A name for the argument. This name is used as a placeholder for the argument in the generated usage string and help (e.g. output of
formatSyntax
andformatHelp
methods). If not specified, by default it is set toARG
. Obviously this is only relevant for options that have an argument. -
Allowed number of occurrences. By default it is "zero or more", that is unrestricted. You can change that to anything you want including "once", "from 2 to 5" etc. Note that some people consider a "required option" to be a violation of normal command line expectations.
-
Whether the option should require its argument to be "attached". Given options
-f
and--foo
usually the arguments can be specified either detached like:-f ARG
and--foo ARG
or attached like:-fARG
and--foo=ARG
. You can choose to only allow the second syntax. This can be handy if your option argument is itself optional and you also have positional arguments. See below
Putting it all together here is an example of an option with one arguments that must happen exactly once.
std::string name;
parser.add(
Option("--name", "-n"). //two names, --level is the main one
argName("NAME"). //argument is named NAME in help output
help("some kind of name").
occurs(once). //must occur exactly once
handler([&](const std::string_view & value) {
name = value;
}));
Note that the configuration methods can be conveniently chained together. Assuming nothing else, other than help option is defining running this program will produce:
$ ./prog --help
Usage: ./prog [--help] --name NAME
options:
--help, -h show this help message and exit
--name NAME, -n NAME some kind of name
The occurs()
call takes a Quantifier
object that specifies minimum and maximum number of times the option can occur. There are some predefined quantifiers such as: zeroOrOneTime
or neverOrOnce
(both mean the same), oneTime
or once
, zeroOrMoreTimes
(this is the default for options) and oneOrMoreTimes
or onceOrMore
. These generally cover most common scenarios. For anything else you can pass a
Quantifer(min, max)
. To specify "unlimited" for max pass Quantifer::infinity
.
Generally, unless the option has some very special semantics it is recommended to use the default - any number of occurrences. Then have a sensible default (if the option isn't used) and override or accumulate semantics if the option occurs more than once. Here is an example of accumulative option that has no arguments:
unsigned verbosity = 0;
parser.add(
Option("-v", "--verbose"). //note that short -v is the main name
help("produce verbose output. Specify more than once, e.g. -vv to make it more verbose").
handler([&]() {
++verbosity;
}));
The help for this will look like this
$ ./prog --help
Usage: ./prog [-v] [--help]
options:
-v, --verbose produce verbose output. Specify more than once, e.g. -vv to
make it more verbose
--help, -h show this help message and exit
Finally here is an example with an optional argument
std::string name = "tgz";
parser.add(
Option("--format").
argName("FORMAT").
help("some kind of format, default is tgz").
handler([&](const std::optional<std::string_view> & value) {
if (value)
name = *value;
}));
This will produce the following help
$ ./prog --help
Usage: ./prog [--format [FORMAT]] [--help]
options:
--format [FORMAT] some kind of format, default is tgz
--help, -h show this help message and exit
Consider what would happen if you have an option, say, --compress
that accepts an optional argument (perhaps compression algorithm) and you also have positional arguments like filenames. What does command line like
$ ./prog --compress file1.txt file2.txt
mean? Is file1.txt
an argument to --compress
or a filename? Most other libraries will say it is a detached argument to --compress
and by default Argum does the same. Using command line above will likely result in "unknown algorithm file1.txt" error from your application. The usual workaround if you want file1.txt
to be positional is to put --
before positional arguments, like this:
$ ./prog --compress -- file1.txt file2.txt
This works but is kind of ugly and can be unexpected to some users.
Argum allows you to require that option argument be specified only using attached syntax. A detached argument will be interpreted as positional in this case. So both of these will work
$ ./prog --compress file1.txt file2.txt
$ ./prog --compress=gzip file1.txt file2.txt
To configure the option like this all you need to do is to call requireAttachedArgument(true)
when configuring an option.
parser.add(
Option("--compress").
argName("ALGORITHM").
requireAttachedArgument(true).
help("compress output using given algorithm (gzip by default)").
handler([&](const std::optional<std::string_view> & value) {
...
}));
Now the help for this option will also indicate the required syntax
$ ./prog --help
Usage: ./prog [--compress [ALGORITHM]] [--help]
options:
--compress[=ALGORITHM] compress output using given algorithm (gzip by default)
--help, -h show this help message and exit
Reporting errors from handlers Parsing common data types Validation
- Home
-
Usage Guide
Basics
Error Handling
Thread Safety
Adding Help
Defining options
Positional arguments
Reporting errors from handlers
Parsing common data types
Validation
Depending on order
Subcommands
Response files
Partial Parsing -
Advanced
Syntax Description
Customizing syntax
Customizing usage and help
Localization