31#ifndef ARGAGG_ARGAGG_ARGAGG_HPP
32#define ARGAGG_ARGAGG_ARGAGG_HPP
44#include <unordered_map>
171 template <
typename T>
185 template <
typename T>
248 template <
typename T>
252 const char delim =
',');
282 template <
typename T>
294 template <
typename T>
295 T
as(
const T& t)
const;
309 template <
typename T>
367 template <
typename T>
378 template <
typename T>
379 T
as(
const T& t)
const;
393 template <
typename T>
471 template <
typename T>
478 template <
typename T>
593 const char flag)
const;
601 const char flag)
const;
769 return this->as<T>();
776 return this->arg !=
nullptr;
783 return !
static_cast<bool>(*this);
790 return this->
all.size();
797 return this->
all[index];
804 return this->
all[index];
811 if (this->
all.size() == 0) {
814 return this->
all.back().as<T>();
821 if (this->
all.size() == 0) {
824 return this->
all.back().as<T>(t);
829option_results::operator T ()
const
831 return this->as<T>();
836option_results::operator bool ()
const
838 return this->all.size() > 0;
845 return !
static_cast<bool>(*this);
852 const auto it = this->
options.find(name);
853 return ( it != this->
options.end()) && it->second.all.size() > 0;
863 msg <<
"no option named \"" << name <<
"\" in parser_results";
872 return this->options.at(name);
875 msg <<
"no option named \"" << name <<
"\" in parser_results";
890 return this->
pos[index];
907 [](
const char* arg) {
908 return convert::arg<T>(arg);
947 const char* name = s + 1;
950 bool is_long =
false;
981 bool encountered_equal =
false;
982 return std::all_of(name, name + len, [&](
const char& c) {
983 if (encountered_equal) {
987 encountered_equal =
true;
1027 const char* name = s + 1;
1030 bool is_long =
false;
1052 if (!is_long && len > 1) {
1058 return std::all_of(name + 1, name + len, [&](
const char& c) {
1074 const char flag)
const
1082 const char flag)
const
1092 const auto existing_long_flag = this->
long_map.find(flag);
1093 return existing_long_flag !=
long_map.end();
1101 const auto existing_long_flag = this->
long_map.find(flag);
1102 if (existing_long_flag ==
long_map.end()) {
1105 return existing_long_flag->second;
1116 for (
auto& defn : definitions) {
1118 if (defn.flags.size() == 0) {
1120 msg <<
"option \"" << defn.name <<
"\" has no flag definitions";
1124 for (
auto& flag : defn.flags) {
1128 msg <<
"flag \"" << flag <<
"\" specified for option \"" << defn.name
1135 const auto existing_short_flag =
1136 map.short_map[short_flag_letter];
1137 bool short_flag_already_exists = (existing_short_flag !=
nullptr);
1138 if (short_flag_already_exists) {
1140 msg <<
"duplicate short flag \"" << flag
1141 <<
"\" found, specified by both option \"" << defn.name
1142 <<
"\" and option \"" << existing_short_flag->name;
1145 map.short_map[
static_cast<std::size_t>(short_flag_letter)] = &defn;
1150 if (map.known_long_flag(flag)) {
1151 const auto existing_long_flag = map.get_definition_for_long_flag(flag);
1153 msg <<
"duplicate long flag \"" << flag
1154 <<
"\" found, specified by both option \"" << defn.name
1155 <<
"\" and option \"" << existing_long_flag->name;
1193 results.options.insert(
1199 bool ignore_flags =
false;
1202 const char* last_flag_expecting_args =
nullptr;
1204 unsigned int num_option_args_to_consume = 0;
1210 const char** arg_i = argv + 1;
1211 const char** arg_end = argv + argc;
1213 while (arg_i != arg_end) {
1214 auto arg_i_cstr = *arg_i;
1220 bool treat_as_positional_argument = (
1222 || num_option_args_to_consume > 0
1225 if (treat_as_positional_argument) {
1231 if (num_option_args_to_consume > 0) {
1232 last_option_expecting_args->
arg = arg_i_cstr;
1233 --num_option_args_to_consume;
1241 if (
std::strncmp(arg_i_cstr,
"--", 2) == 0 && arg_i_len == 2) {
1242 ignore_flags =
true;
1249 results.pos.push_back(arg_i_cstr);
1255 last_flag_expecting_args =
nullptr;
1256 last_option_expecting_args =
nullptr;
1257 num_option_args_to_consume = 0;
1263 bool is_long_flag = (arg_i_cstr[1] ==
'-');
1274 auto long_flag_arg =
std::strchr(arg_i_cstr,
'=');
1276 if (long_flag_arg !=
nullptr) {
1277 flag_len =
static_cast<std::size_t>(long_flag_arg - arg_i_cstr);
1283 msg <<
"found unexpected flag: " << long_flag_str;
1289 if (long_flag_arg !=
nullptr && defn->num_args == 0) {
1291 msg <<
"found argument for option not expecting an argument: "
1299 auto& opt_results = results.options[defn->name];
1301 opt_results.all.push_back(
std::move(opt_result));
1303 if (defn->requires_arguments()) {
1304 bool there_is_an_equal_delimited_arg = (long_flag_arg !=
nullptr);
1305 if (there_is_an_equal_delimited_arg) {
1308 opt_results.all.back().
arg = long_flag_arg + 1;
1310 last_flag_expecting_args = arg_i_cstr;
1311 last_option_expecting_args = &(opt_results.all.back());
1312 num_option_args_to_consume = defn->num_args;
1327 for (
std::size_t sf_idx = 1; sf_idx < arg_i_len; ++sf_idx) {
1328 const auto short_flag = arg_i_cstr[sf_idx];
1332 msg <<
"found non-alphanumeric character '" << arg_i_cstr[sf_idx]
1333 <<
"' in flag group '" << arg_i_cstr <<
"'";
1339 msg <<
"found unexpected flag '" << arg_i_cstr[sf_idx]
1340 <<
"' in flag group '" << arg_i_cstr <<
"'";
1345 auto& opt_results = results.options[defn->name];
1350 opt_results.all.push_back(
std::move(opt_result));
1352 if (defn->requires_arguments()) {
1358 bool is_last_short_flag_in_group = (sf_idx == arg_i_len - 1);
1359 if (is_last_short_flag_in_group) {
1360 last_flag_expecting_args = arg_i_cstr;
1361 last_option_expecting_args = &(opt_results.all.back());
1362 num_option_args_to_consume = defn->num_args;
1372 opt_results.all.back().
arg = arg_i_cstr + sf_idx + 1;
1384 if (num_option_args_to_consume > 0) {
1386 msg <<
"last option \"" << last_flag_expecting_args
1387 <<
"\" expects an argument but the parser ran out of command line "
1388 <<
"arguments to parse";
1399 return parse(argc,
const_cast<const char**
>(argv));
1412 template <
typename T>
inline
1420 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1436 template <
typename T>
inline
1444 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1454#define DEFINE_CONVERSION_FROM_LONG_(TYPE) \
1455 template <> inline \
1456 TYPE arg(const char* arg) \
1458 return long_<TYPE>(arg); \
1471#undef DEFINE_CONVERSION_FROM_LONG_
1474#define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE) \
1475 template <> inline \
1476 TYPE arg(const char* arg) \
1478 return long_long_<TYPE>(arg); \
1484#undef DEFINE_CONVERSION_FROM_LONG_LONG_
1487 template <
typename T>
1509 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1527 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1551 template <
typename T>
1557 const char* begin = s;
1577:
std::ostringstream(), output(output)
1599 [](
int ch) { return !std::isspace(ch); }));
1614 [](
int ch) { return !std::isspace(ch); }).base(),
1625 return indent +
rstrip(contents) +
"\n";
1638 if (indentation_spaces == std::string::npos) {
1639 indentation_spaces = 0;
1642 const auto line =
lstrip(single_line);
1643 const auto indent =
std::string(indentation_spaces,
' ');
1650 const auto new_position = line.find_first_of(
" ", position);
1651 if (new_position == std::string::npos) {
1655 if (new_position + indentation_spaces > line_start + wrap_width) {
1657 indent, line.substr(line_start, position - line_start - 1));
1659 line_start = position;
1662 position = new_position + 1;
1678 const auto column_width = 75;
1681 result +=
wrap_line(line, column_width);
1696 for (
auto& flag : definition.flags) {
1698 if (flag != definition.flags.back()) {
1702 os <<
"\n " << definition.help <<
'\n';
#define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE)
#define DEFINE_CONVERSION_FROM_LONG_(TYPE)
std::ostream & operator<<(std::ostream &os, const argagg::parser &x)
Writes the option help to the given stream.
T find_first_not_of(T... args)
T long_(const char *arg)
Templated function for conversion to T using the std::strtol() function. This is used for anything lo...
bool parse_next_component(const char *&s, T &out_arg, const char delim=',')
A utility function for parsing an argument as a delimited list. To use, initialize a const char* poin...
T arg(const char *arg)
Explicit instantiations of this function are used to convert arguments to types.
T long_long_(const char *arg)
Templated function for conversion to T using the std::strtoll() function. This is used for anything l...
There are only two hard things in Computer Science: cache invalidation and naming things (Phil Karlto...
parser_map validate_definitions(const std::vector< definition > &definitions)
Validates a collection (specifically an std::vector) of definition objects by checking if the contain...
std::string rstrip(const std::string &text)
bool flag_is_short(const char *s)
Tests whether or not a valid flag is short. Assumes the provided cstring is already a valid flag.
bool cmd_line_arg_is_option_flag(const char *s)
Checks whether or not a command line argument should be processed as an option flag....
std::string construct_line(const std::string &indent, const std::string &contents)
std::string lstrip(const std::string &text)
std::string fmt_string(const std::string &s)
Processes the provided string using the fmt utility and returns the resulting output as a string....
std::string wrap_line(const std::string &single_line, const std::size_t wrap_width)
Return a wrapped version of a single line of text.
bool is_valid_flag_definition(const char *s)
Checks whether a flag in an option definition is valid. I suggest reading through the function source...
For simple types the main extension point for adding argument conversions is argagg::convert::arg<T>(...
static T convert(const char *arg)
An option definition which essentially represents what an option is.
bool requires_arguments() const
Returns true if this option requires arguments.
bool wants_no_arguments() const
Returns true if this option does not want any arguments.
const std::string name
Name of the option. Option parser results are keyed by this name.
std::vector< std::string > flags
List of strings to match that correspond to this option. Should be fully specified with hyphens (e....
std::string help
Help string for this option.
unsigned int num_args
Number of arguments this option requires. Must be 0 or 1. All other values have undefined behavior....
A convenience output stream that will accumulate what is streamed to it and then, on destruction,...
std::ostream & output
Reference to the final output stream that the formatted string will be streamed to.
~fmt_ostream()
Special destructor that will format the accumulated string using fmt (via the argagg::fmt_string() fu...
fmt_ostream(std::ostream &output)
Construct to output to the provided output stream when this object is destroyed.
This exception is thrown when an option's flag is invalid. This can be the case if the flag is not pr...
This exception is thrown when an option requires an argument but is not provided one....
Represents a single option parse result.
const char * arg
Argument parsed for this single option. If no argument was parsed this will be set to nullptr.
bool operator!() const
Explicitly define a unary not operator that wraps the implicit boolean conversion specialization in c...
T as() const
Converts the argument parsed for this single option instance into the given type using the type match...
Represents multiple option parse results for a single option. If treated as a single parse result it ...
std::size_t count() const
Gets the number of times the option shows up.
option_result & operator[](std::size_t index)
Gets a single option parse result by index.
std::vector< option_result > all
All option parse results for this option.
T as() const
Converts the argument parsed for the LAST option parse result for the parent definition to the provid...
bool operator!() const
Explicitly define a unary not operator that wraps the implicit boolean conversion specialization in c...
Contains two maps which aid in option parsing. The first map, short_map, maps from a short flag (just...
const definition * get_definition_for_long_flag(const std::string &flag) const
If the long flag exists in the map object then it is returned by this method. If it doesn't then null...
std::unordered_map< std::string, const definition * > long_map
Maps from a long flag (an std::string) to a pointer to the original definition that the flag represen...
bool known_short_flag(const char flag) const
Returns true if the provided short flag exists in the map object.
bool known_long_flag(const std::string &flag) const
Returns true if the provided long flag exists in the map object.
const definition * get_definition_for_short_flag(const char flag) const
If the short flag exists in the map object then it is returned by this method. If it doesn't then nul...
std::array< const definition *, 256 > short_map
Maps from a short flag (just a character) to a pointer to the original definition that the flag repre...
Represents all results of the parser including options and positional arguments.
std::size_t count() const
Gets the number of positional arguments.
std::vector< const char * > pos
Vector of positional arguments.
std::vector< T > all_as() const
Gets all positional arguments converted to the given type.
option_results & operator[](const std::string &name)
Get the parser results for the given definition. If the definition never showed up then the exception...
bool has_option(const std::string &name) const
Used to check if an option was specified at all.
T as(std::size_t i=0) const
Gets a positional argument converted to the given type.
std::unordered_map< std::string, option_results > options
Maps from definition name to the structure which contains the parser results for that definition.
const char * program
Returns the name of the program from the original arguments list. This is always the first argument.
A list of option definitions used to inform how to parse arguments.
std::vector< definition > definitions
Vector of the option definitions which inform this parser how to parse the command line arguments.
parser_results parse(int argc, const char **argv) const
Parses the provided command line arguments and returns the results as parser_results.
This exception is thrown when a long option is parsed and is given an argument using the "=" syntax b...
This exception is thrown when an option is parsed unexpectedly such as when an argument was expected ...
This exception is thrown when an unknown option is requested by name from an argagg::parser_results t...