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;
735 template <
typename T>
739 return convert::arg<T>(this->
arg);
746 template <
typename T>
751 return convert::arg<T>(this->
arg);
766 template <
typename T>
767 option_result::operator T ()
const 769 return this->as<T>();
774 option_result::operator bool ()
const 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];
808 template <
typename T>
811 if (this->
all.size() == 0) {
814 return this->
all.back().as<T>();
818 template <
typename T>
821 if (this->
all.size() == 0) {
824 return this->
all.back().as<T>(t);
828 template <
typename T>
829 option_results::operator T ()
const 831 return this->as<T>();
836 option_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";
869 const option_results&
872 return this->options.at(name);
875 msg <<
"no option named \"" << name <<
"\" in parser_results";
890 return this->
pos[index];
894 template <
typename T>
897 return convert::arg<T>(this->
pos[i]);
901 template <
typename T>
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 1415 char* endptr =
nullptr;
1418 if (endptr ==
arg) {
1420 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1423 if (errno == ERANGE) {
1436 template <
typename T>
inline 1439 char* endptr =
nullptr;
1442 if (endptr ==
arg) {
1444 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1447 if (errno == ERANGE) {
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>
1497 return argagg::convert::arg<int>(
arg) != 0;
1504 char* endptr =
nullptr;
1507 if (endptr ==
arg) {
1509 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1512 if (errno == ERANGE) {
1522 char* endptr =
nullptr;
1525 if (endptr ==
arg) {
1527 msg <<
"unable to convert argument to integer: \"" <<
arg <<
"\"";
1530 if (errno == ERANGE) {
1551 template <
typename T>
1557 const char* begin = s;
1561 out_arg = argagg::convert::arg<T>(arg_str.
c_str());
1564 std::string arg_str(begin, static_cast<std::size_t>(s - begin));
1565 out_arg = argagg::convert::arg<T>(arg_str.
c_str());
1577 :
std::ostringstream(), output(output)
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';
1708 #endif // ARGAGG_ARGAGG_ARGAGG_HPP #define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE)
option_result & operator[](std::size_t index)
Gets a single option parse result by index.
std::string lstrip(const std::string &text)
bool known_short_flag(const char flag) const
Returns true if the provided short flag exists in the map object.
std::size_t count() const
Gets the number of positional arguments.
std::string construct_line(const std::string &indent, const std::string &contents)
This exception is thrown when an unknown option is requested by name from an argagg::parser_results t...
Represents a single option parse result.
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. This is very similar to is_valid_flag_definition() but must allow for short flag groups (e.g. "-abc") and equal-assigned long flag arguments (e.g. "--output=foo.txt").
const char * arg
Argument parsed for this single option. If no argument was parsed this will be set to nullptr...
bool has_option(const std::string &name) const
Used to check if an option was specified at all.
T find_first_not_of(T... args)
bool requires_arguments() const
Returns true if this option requires arguments.
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...
This exception is thrown when an option requires an argument but is not provided one. This can happen if another flag was found after the option or if we simply reach the end of the command line arguments.
std::vector< option_result > all
All option parse results for this option.
static T convert(const char *arg)
bool known_long_flag(const std::string &flag) const
Returns true if the provided long flag exists in the map object.
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 flag_is_short(const char *s)
Tests whether or not a valid flag is short. Assumes the provided cstring is already a valid flag...
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...
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...
For simple types the main extension point for adding argument conversions is argagg::convert::arg<T>(...
const char * program
Returns the name of the program from the original arguments list. This is always the first argument...
std::string fmt_string(const std::string &s)
Processes the provided string using the fmt utility and returns the resulting output as a string...
This exception is thrown when a long option is parsed and is given an argument using the "=" syntax b...
unsigned int num_args
Number of arguments this option requires. Must be 0 or 1. All other values have undefined behavior...
parser_results parse(int argc, const char **argv) const
Parses the provided command line arguments and returns the results as parser_results.
bool wants_no_arguments() const
Returns true if this option does not want any arguments.
std::vector< T > all_as() const
Gets all positional arguments converted to the given type.
bool operator!() const
Explicitly define a unary not operator that wraps the implicit boolean conversion specialization in c...
T arg(const char *arg)
Explicit instantiations of this function are used to convert arguments to types.
~fmt_ostream()
Special destructor that will format the accumulated string using fmt (via the argagg::fmt_string() fu...
A convenience output stream that will accumulate what is streamed to it and then, on destruction...
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...
fmt_ostream(std::ostream &output)
Construct to output to the provided output stream when this object is destroyed.
const std::string name
Name of the option. Option parser results are keyed by this name.
std::ostream & operator<<(std::ostream &os, const argagg::parser &x)
Writes the option help to the given stream.
T as(std::size_t i=0) const
Gets a positional argument converted to the given type.
There are only two hard things in Computer Science: cache invalidation and naming things (Phil Karlto...
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...
std::ostream & output
Reference to the final output stream that the formatted string will be streamed to.
An option definition which essentially represents what an option is.
std::vector< std::string > flags
List of strings to match that correspond to this option. Should be fully specified with hyphens (e...
std::string rstrip(const std::string &text)
T long_long_(const char *arg)
Templated function for conversion to T using the std::strtoll() function. This is used for anything l...
std::string help
Help string for this option.
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...
std::size_t count() const
Gets the number of times the option shows up.
parser_map validate_definitions(const std::vector< definition > &definitions)
Validates a collection (specifically an std::vector) of definition objects by checking if the contain...
This exception is thrown when an option's flag is invalid. This can be the case if the flag is not pr...
#define DEFINE_CONVERSION_FROM_LONG_(TYPE)
std::string arg(const char *arg)
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::vector< definition > definitions
Vector of the option definitions which inform this parser how to parse the command line arguments...
std::unordered_map< std::string, option_results > options
Maps from definition name to the structure which contains the parser results for that definition...
Represents multiple option parse results for a single option. If treated as a single parse result it ...
T as() const
Converts the argument parsed for the LAST option parse result for the parent definition to the provid...
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.
std::vector< const char * > pos
Vector of positional arguments.
This exception is thrown when an option is parsed unexpectedly such as when an argument was expected ...
T long_(const char *arg)
Templated function for conversion to T using the std::strtol() function. This is used for anything lo...
T as() const
Converts the argument parsed for this single option instance into the given type using the type match...
A list of option definitions used to inform how to parse arguments.
Represents all results of the parser including options and positional arguments.