Some handy code for handling cli arguments.
Arrrgh supports free parameters, options (--option
) and aliases to options (-a
).
Options and aliases trigger processor functions that are responsible of determining the proper value for them.
Processors (as well as option values) are not bound to strings.
Processor functions take the arguments stream as a parameter and may look
for any number of next values.
Sets the next argument after --option
as it's value discarding all previous values.
- Python:
single_argument_processor
- C++:
single_argument_processor
Assigns True
to an --option
when it's met. If used with the corresponding option registering function
False
is automatically assigned as the default value.
- Python:
flag_processor
- C++:
flag_processor
Collects all values that follow immediately after --option
. Expects the default value to be a list.
Default value is a list if the option is registered with the corresponding function
- Python:
list_processor
- C++:
list_processor
Sets the next argument after --option
as it's value discarding all previous values
if it's an integer. Otherwise leaves the default value as is
- Python:
integer_processor
- C++:
integer_processor
Sets the next argument after --option
as it's value discarding all previous values
if it's a float. Otherwise leaves the default value as is
- Python:
float_processor
- C++:
float_processor
Define options as:
import arrrgh
arrrgh.add_option("source");
arrrgh.add_flag("flag");
arrrgh.add_list("tag");
arrrgh.add_integer("number", 10);
arrrgh.add_alias('s', "source");
arrrgh.add_alias('f', "flag");
arrrgh.add_alias('t', "tag");
arrrgh.add_alias('n', "number");
arrrgh.parse()
And access values via:
for key, value in arrrgh.options.items():
print("option: {} -> \"{}\"".format(key, value))
for key, value in arrrgh.aliases.items():
print("alias: {} -> {}".format(key, value))
for it in arrrgh.parameters:
print("parameter: {}".format(it))
def add_option(name, default_value='', processor=single_argument_processor)
- Registers a new option.
def add_alias(alias_name, option_name)
- Registers a new alias. Requires an option to exist
def add_flag(name)
- Registers a new boolean option. Value is
False
by default andTrue
after the--name
is met at least once.
def add_list(name)
- Registers a new list option. Empty list by default. Collects all values that follow immidiately after
--name
def add_integer(name, default_value=0):
- Registers a new integer option. 0 by default. Equals to the last met value for
--name
def add_float(name, default_value=0):
- Registers a new float option. 0.0 by default. Equals to the last met value for
--name
def parse(stream=sys.argv)
- Analyses the arguments.
To declare a custom processor create a function taking (generator
, old_value
) parameters and returning some value.
Define options as:
#include "arrrgh.hpp"
arrrgh::add_option("source");
arrrgh::add_flag("flag");
arrrgh::add_list("tag");
arrrgh::add_integer("number", 10);
arrrgh::add_alias('s', "source");
arrrgh::add_alias('f', "flag");
arrrgh::add_alias('t', "tag");
arrrgh::add_alias('n', "number");
arrrgh::parse(argv, argv + argc);
And access values via:
for (auto it : arrrgh::options<arrrgh::StringLike>) {
std::cout << "option: " << it.first << " -> \"" << it.second << '\"' << std::endl;
}
for (auto it : arrrgh::options<int>) {
std::cout << "option: " << it.first << " -> " << it.second << std::endl;
}
for (auto it : arrrgh::options<bool>) {
std::cout << "option: " << it.first << " -> " << it.second << std::endl;
}
for (auto it : arrrgh::options<arrrgh::List>) {
std::cout << "option: " << it.first << " -> [" << std::endl;
for (auto that : it.second) {
std::cout << " " << that << std::endl;
}
std::cout << ']' << std::endl;
}
for (auto it : arrrgh::aliases) {
std::cout << "alias: " << it.first << " -> " << it.second << std::endl;
}
for (auto it : arrrgh::parameters) {
std::cout << "parameter: " << it << std::endl;
}
C++ code introduces a bunch of type aliases. It's been made to make the code more readable during the development.
String
->std::string
StringLike
->std::string_view
Map<T, K>
->std::unordered_map<T, K>
List
->std::vector<StringLike>
template <typename Value = StringLike>
void add_option(
StringLike option_name,
Value default_value = StringLike(),
Processor processor = single_argument_processor
)
- Registers a new option.
void add_alias(
char alias_name,
StringLike option_name
)
- Registers a new alias. Requires an option to exist
void add_flag(StringLike option_name)
- Registers a new boolean option. Value is
False
by default andTrue
after the--name
is met at least once.
void add_list(StringLike option_name)
- Registers a new list option. Empty list by default. Collects all values that follow immidiately after
--name
void add_integer(StringLike option_name, int default_value = 0)
- Registers a new integer option. If the new value can be converted from
a string via
std::from_chars
, converts it and assigns to--name
void add_float(StringLike option_name, double default_value = 0.0)
- Registers a new float option. If the new value can be converted from
a string via
std::strtod
, converts it and assigns to--name
template <typename Iterator>
void parse(Iterator begin, Iterator end)
- Analyses the arguments.
using Processor = void (*)(StringLike, const Arguments &)
- Processor declaration. It accepts the name of the option as the first argument and a const reference the arguments stream.
struct Arguments {
virtual ~Arguments() {};
/*
Returns true if there other
values left unread
*/
virtual bool has_next() const = 0;
/*
Returns a StringLike of the
next value of the stream.
This helps to generalize
const char *[] and std::string streams
*/
virtual StringLike next() const = 0;
};
- Represents a sequence of
StringLike
. Actually just a bit different form of passing iterators around (in and out of the functions) in a way that any inner function 'modifies the pointers of the outer function'