From 6086d0778eed3c4d523186fd016a5289964b9277 Mon Sep 17 00:00:00 2001 From: tqcq <99722391+tqcq@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:22:55 +0800 Subject: [PATCH] feat update --- 3party/argagg/include/argagg/argagg.hpp | 1682 +++++++++++------------ 1 file changed, 787 insertions(+), 895 deletions(-) diff --git a/3party/argagg/include/argagg/argagg.hpp b/3party/argagg/include/argagg/argagg.hpp index 70e2852..66029a7 100644 --- a/3party/argagg/include/argagg/argagg.hpp +++ b/3party/argagg/include/argagg/argagg.hpp @@ -34,7 +34,7 @@ #ifdef __unix__ #include #include -#endif // #ifdef __unix__ +#endif// #ifdef __unix__ #include #include @@ -50,7 +50,6 @@ #include #include - /** * @brief * There are only two hard things in Computer Science: cache invalidation and @@ -100,42 +99,35 @@ */ namespace argagg { - /** * @brief * This exception is thrown when a long option is parsed and is given an * argument using the "=" syntax but the option doesn't expect an argument. */ -struct unexpected_argument_error -: public std::runtime_error { - using std::runtime_error::runtime_error; +struct unexpected_argument_error : public std::runtime_error { + using std::runtime_error::runtime_error; }; - /** * @brief * This exception is thrown when an option is parsed unexpectedly such as when * an argument was expected for a previous option or if an option was found * that has not been defined. */ -struct unexpected_option_error -: public std::runtime_error { - using std::runtime_error::runtime_error; +struct unexpected_option_error : public std::runtime_error { + using std::runtime_error::runtime_error; }; - /** * @brief * 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. */ -struct option_lacks_argument_error -: public std::runtime_error { - using std::runtime_error::runtime_error; +struct option_lacks_argument_error : public std::runtime_error { + using std::runtime_error::runtime_error; }; - /** * @brief * This exception is thrown when an option's flag is invalid. This can be the @@ -143,12 +135,10 @@ struct option_lacks_argument_error * alpha-numeric characters after the hypens. See is_valid_flag_definition() * for more details. */ -struct invalid_flag -: public std::runtime_error { - using std::runtime_error::runtime_error; +struct invalid_flag : public std::runtime_error { + using std::runtime_error::runtime_error; }; - /** * @brief * The set of template instantiations that convert C-strings to other types for @@ -157,16 +147,15 @@ struct invalid_flag */ namespace convert { - /** +/** * @brief * Explicit instantiations of this function are used to convert arguments to * types. */ - template - T arg(const char* arg); - -} +template +T arg(const char *arg); +}// namespace convert /** * @brief @@ -177,14 +166,14 @@ namespace convert { */ struct option_result { - /** + /** * @brief * Argument parsed for this single option. If no argument was parsed this * will be set to nullptr. */ - const char* arg; + const char *arg; - /** + /** * @brief * Converts the argument parsed for this single option instance into the * given type using the type matched conversion function @@ -193,10 +182,10 @@ struct option_result { * exception is thrown. The specific conversion function may throw other * exceptions. */ - template - T as() const; + template + T as() const; - /** + /** * @brief * Converts the argument parsed for this single option instance into the * given type using the type matched conversion function @@ -205,10 +194,10 @@ struct option_result { * instead. If the conversion function throws an exception then it is ignored * and the default value is returned. */ - template - T as(const T& t) const; + template + T as(const T &t) const; - /** + /** * @brief * Since we have the argagg::option_result::as() API we might as well alias * it as an implicit conversion operator. This performs implicit conversion @@ -220,12 +209,10 @@ struct option_result { * otherwise. This specialization DOES NOT convert the argument to a bool. If * you need to convert the argument to a bool then use the as() API. */ - template - operator T () const; - + template + operator T() const; }; - /** * @brief * Represents multiple option parse results for a single option. If treated as @@ -238,31 +225,31 @@ struct option_result { */ struct option_results { - /** + /** * @brief * All option parse results for this option. */ - std::vector all; + std::vector all; - /** + /** * @brief * Gets the number of times the option shows up. */ - std::size_t count() const; + std::size_t count() const; - /** + /** * @brief * Gets a single option parse result by index. */ - option_result& operator [] (std::size_t index); + option_result &operator[](std::size_t index); - /** + /** * @brief * Gets a single option result by index. */ - const option_result& operator [] (std::size_t index) const; + const option_result &operator[](std::size_t index) const; - /** + /** * @brief * Converts the argument parsed for the LAST option parse result for the * parent definition to the provided type. For example, if this was for "-f 1 @@ -271,10 +258,10 @@ struct option_results { * thrown. Any exceptions thrown by option_result::as() are not * handled. */ - template - T as() const; + template + T as() const; - /** + /** * @brief * Converts the argument parsed for the LAST option parse result for the * parent definition to the provided type. For example, if this was for "-f 1 @@ -282,10 +269,10 @@ struct option_results { * there are no option parse results then the provided default value is * returned instead. */ - template - T as(const T& t) const; + template + T as(const T &t) const; - /** + /** * @brief * Since we have the option_results::as() API we might as well alias * it as an implicit conversion operator. This performs implicit conversion @@ -297,12 +284,10 @@ struct option_results { * otherwise. This specialization DOES NOT convert the argument to a bool. If * you need to convert the argument to a bool then use the as() API. */ - template - operator T () const; - + template + operator T() const; }; - /** * @brief * Represents all results of the parser including options and positional @@ -310,125 +295,121 @@ struct option_results { */ struct parser_results { - /** + /** * @brief * Returns the name of the program from the original arguments list. This is * always the first argument. */ - const char* program; + const char *program; - /** + /** * @brief * Maps from definition name to the structure which contains the parser * results for that definition. */ - std::unordered_map options; + std::unordered_map options; - /** + /** * @brief * Vector of positional arguments. */ - std::vector pos; + std::vector pos; - /** + /** * @brief * Used to check if an option was specified at all. */ - bool has_option(const std::string& name) const; + bool has_option(const std::string &name) const; - /** + /** * @brief * Get the parser results for the given definition. If the definition never * showed up then the exception from the unordered_map access will bubble * through so check if the flag exists in the first place with has_option(). */ - option_results& operator [] (const std::string& name); + option_results &operator[](const std::string &name); - /** + /** * @brief * Get the parser results for the given definition. If the definition never * showed up then the exception from the unordered_map access will bubble * through so check if the flag exists in the first place with has_option(). */ - const option_results& operator [] (const std::string& name) const; + const option_results &operator[](const std::string &name) const; - /** + /** * @brief * Gets the number of positional arguments. */ - std::size_t count() const; + std::size_t count() const; - /** + /** * @brief * Gets a positional argument by index. */ - const char* operator [] (std::size_t index) const; + const char *operator[](std::size_t index) const; - /** + /** * @brief * Gets a positional argument converted to the given type. */ - template - T as(std::size_t i = 0) const; + template + T as(std::size_t i = 0) const; - /** + /** * @brief * Gets all positional arguments converted to the given type. */ - template - std::vector all_as() const; - + template + std::vector all_as() const; }; - /** * @brief * An option definition which essentially represents what an option is. */ struct definition { - /** + /** * @brief * Name of the option. Option parser results are keyed by this name. */ - const std::string name; + const std::string name; - /** + /** * @brief * List of strings to match that correspond to this option. Should be fully * specified with hyphens (e.g. "-v" or "--verbose"). */ - std::vector flags; + std::vector flags; - /** + /** * @brief * Help string for this option. */ - std::string help; + std::string help; - /** + /** * @brief * Number of arguments this option requires. Must be 0 or 1. All other values * have undefined behavior. Okay, the code actually works with positive * values in general, but it's unorthodox command line behavior. */ - unsigned int num_args; + unsigned int num_args; - /** + /** * @brief * Returns true if this option does not want any arguments. */ - bool wants_no_arguments() const; + bool wants_no_arguments() const; - /** + /** * @brief * Returns true if this option requires arguments. */ - bool requires_arguments() const; - + bool requires_arguments() const; }; - /** * @brief * Checks whether or not a command line argument should be processed as an @@ -436,27 +417,21 @@ struct definition { * allow for short flag groups (e.g. "-abc") and equal-assigned long flag * arguments (e.g. "--output=foo.txt"). */ -bool cmd_line_arg_is_option_flag( - const char* s); - +bool cmd_line_arg_is_option_flag(const char *s); /** * @brief * Checks whether a flag in an option definition is valid. I suggest reading * through the function source to understand what dictates a valid. */ -bool is_valid_flag_definition( - const char* s); - +bool is_valid_flag_definition(const char *s); /** * @brief * Tests whether or not a valid flag is short. Assumes the provided cstring is * already a valid flag. */ -bool flag_is_short( - const char* s); - +bool flag_is_short(const char *s); /** * @brief @@ -471,53 +446,48 @@ bool flag_is_short( */ struct parser_map { - /** + /** * @brief * Maps from a short flag (just a character) to a pointer to the original * @ref definition that the flag represents. */ - std::array short_map; + std::array short_map; - /** + /** * @brief * Maps from a long flag (an std::string) to a pointer to the original @ref * definition that the flag represents. */ - std::unordered_map long_map; + std::unordered_map long_map; - /** + /** * @brief * Returns true if the provided short flag exists in the map object. */ - bool known_short_flag( - const char flag) const; + bool known_short_flag(const char flag) const; - /** + /** * @brief * If the short flag exists in the map object then it is returned by this * method. If it doesn't then nullptr will be returned. */ - const definition* get_definition_for_short_flag( - const char flag) const; + const definition *get_definition_for_short_flag(const char flag) const; - /** + /** * @brief * Returns true if the provided long flag exists in the map object. */ - bool known_long_flag( - const std::string& flag) const; + bool known_long_flag(const std::string &flag) const; - /** + /** * @brief * If the long flag exists in the map object then it is returned by this * method. If it doesn't then nullptr will be returned. */ - const definition* get_definition_for_long_flag( - const std::string& flag) const; - + const definition * + get_definition_for_long_flag(const std::string &flag) const; }; - /** * @brief * Validates a collection (specifically an std::vector) of @ref definition @@ -525,9 +495,7 @@ struct parser_map { * definition objects is not valid then an exception is thrown. Upon successful * validation a @ref parser_map object is returned. */ -parser_map validate_definitions( - const std::vector& definitions); - +parser_map validate_definitions(const std::vector &definitions); /** * @brief @@ -535,14 +503,14 @@ parser_map validate_definitions( */ struct parser { - /** + /** * @brief * Vector of the option definitions which inform this parser how to parse * the command line arguments. */ - std::vector definitions; + std::vector definitions; - /** + /** * @brief * Parses the provided command line arguments and returns the results as * @ref parser_results. @@ -551,20 +519,18 @@ struct parser { * This method is not thread-safe and assumes that no modifications are made * to the definitions member field during the extent of this method call. */ - parser_results parse(int argc, const char** argv) const; + parser_results parse(int argc, const char **argv) const; - /** + /** * @brief * Through strict interpretation of pointer casting rules, despite this being * a safe operation, C++ doesn't allow implicit casts from char** to * const char** so here's an overload that performs a const_cast, * which is typically frowned upon but is safe here. */ - parser_results parse(int argc, char** argv) const; - + parser_results parse(int argc, char **argv) const; }; - /** * @brief * A convenience output stream that will accumulate what is streamed to it and @@ -590,31 +556,29 @@ struct parser { */ struct fmt_ostream : public std::ostringstream { - /** + /** * @brief * Reference to the final output stream that the formatted string will be * streamed to. */ - std::ostream& output; + std::ostream &output; - /** + /** * @brief * Construct to output to the provided output stream when this object is * destroyed. */ - fmt_ostream(std::ostream& output); + fmt_ostream(std::ostream &output); - /** + /** * @brief * Special destructor that will format the accumulated string using fmt (via * the argagg::fmt_string() function) and stream it to the std::ostream * stored. */ - ~fmt_ostream(); - + ~fmt_ostream(); }; - /** * @brief * Processes the provided string using the fmt util and returns the resulting @@ -630,919 +594,847 @@ struct fmt_ostream : public std::ostringstream { * executing a process, reading/writing to/from file descriptors, and the * existence of the fmt util. */ -std::string fmt_string(const std::string& s); - - -} // namespace argagg +std::string fmt_string(const std::string &s); +}// namespace argagg /** * @brief * Writes the option help to the given stream. */ -std::ostream& operator << (std::ostream& os, const argagg::parser& x); - +std::ostream &operator<<(std::ostream &os, const argagg::parser &x); // ---- end of declarations, header-only implementations follow ---- - namespace argagg { - -template -T option_result::as() const +template +T +option_result::as() const { - if (this->arg) { - return convert::arg(this->arg); - } else { - throw option_lacks_argument_error("option has no argument"); - } -} - - -template -T option_result::as(const T& t) const -{ - if (this->arg) { - try { - return convert::arg(this->arg); - } catch (...) { - return t; + if (this->arg) { + return convert::arg(this->arg); + } else { + throw option_lacks_argument_error("option has no argument"); } - } else { - // I actually think this will never happen. To call this method you have - // to access a specific option_result for an option. If there's a - // specific option_result then the option was found. If the option - // requires an argument then it will definitely have an argument - // otherwise the parser would have complained. - return t; - } } - -template -option_result::operator T () const +template +T +option_result::as(const T &t) const { - return this->as(); -} - - -template <> inline -option_result::operator bool () const -{ - return this->arg != nullptr; -} - - -inline -std::size_t option_results::count() const -{ - return this->all.size(); -} - - -inline -option_result& option_results::operator [] (std::size_t index) -{ - return this->all[index]; -} - - -inline -const option_result& option_results::operator [] (std::size_t index) const -{ - return this->all[index]; -} - - -template -T option_results::as() const -{ - if (this->all.size() == 0) { - throw std::out_of_range("no option arguments to convert"); - } - return this->all.back().as(); -} - - -template -T option_results::as(const T& t) const -{ - if (this->all.size() == 0) { - return t; - } - return this->all.back().as(t); -} - - -template -option_results::operator T () const -{ - return this->as(); -} - - -template <> inline -option_results::operator bool () const -{ - return this->all.size() > 0; -} - - -inline -bool parser_results::has_option(const std::string& name) const -{ - const auto it = this->options.find(name); - return ( it != this->options.end()) && it->second.all.size() > 0; -} - - -inline -option_results& parser_results::operator [] (const std::string& name) -{ - return this->options.at(name); -} - - -inline -const option_results& -parser_results::operator [] (const std::string& name) const -{ - return this->options.at(name); -} - - -inline -std::size_t parser_results::count() const -{ - return this->pos.size(); -} - - -inline -const char* parser_results::operator [] (std::size_t index) const -{ - return this->pos[index]; -} - - -template -T parser_results::as(std::size_t i) const -{ - return convert::arg(this->pos[i]); -} - - -template -std::vector parser_results::all_as() const -{ - std::vector v(this->pos.size()); - std::transform( - this->pos.begin(), this->pos.end(), v.begin(), - [](const char* arg) { - return convert::arg(arg); - }); - return v; -} - - -inline -bool definition::wants_no_arguments() const -{ - return this->num_args == 0; -} - - -inline -bool definition::requires_arguments() const -{ - return this->num_args > 0; -} - - -inline -bool cmd_line_arg_is_option_flag( - const char* s) -{ - auto len = std::strlen(s); - - // The shortest possible flag has two characters: a hyphen and an - // alpha-numeric character. - if (len < 2) { - return false; - } - - // All flags must start with a hyphen. - if (s[0] != '-') { - return false; - } - - // Shift the name forward by a character to account for the initial hyphen. - // This means if s was originally "-v" then name will be "v". - const char* name = s + 1; - - // Check if we're dealing with a long flag. - bool is_long = false; - if (s[1] == '-') { - is_long = true; - - // Just -- is not a valid flag. - if (len == 2) { - return false; - } - - // Shift the name forward to account for the extra hyphen. This means if s - // was originally "--output" then name will be "output". - name = s + 2; - } - - // The first character of the flag name must be alpha-numeric. This is to - // prevent things like "---a" from being valid flags. - len = std::strlen(name); - if (!std::isalnum(name[0])) { - return false; - } - - // At this point in is_valid_flag_definition() we would check if the short - // flag has only one character. At command line specification you can group - // short flags together or even add an argument to a short flag without a - // space delimiter. Thus we don't check if this has only one character - // because it might not. - - // If this is a long flag then we expect all characters *up to* an equal sign - // to be alpha-numeric or a hyphen. After the equal sign you are specify the - // argument to a long flag which can be basically anything. - if (is_long) { - bool encountered_equal = false; - return std::all_of(name, name + len, [&](const char& c) { - if (encountered_equal) { - return true; - } else { - if (c == '=') { - encountered_equal = true; - return true; - } - return std::isalnum(c) || c == '-'; + if (this->arg) { + try { + return convert::arg(this->arg); + } catch (...) { + return t; } - }); - } - - // At this point we are not dealing with a long flag. We already checked that - // the first character is alpha-numeric so we've got the case of a single - // short flag covered. This might be a short flag group though and we might - // be tempted to check that each character of the short flag group is - // alpha-numeric. However, you can specify the argument for a short flag - // without a space delimiter (e.g. "-I/usr/local/include") so you can't tell - // if the rest of a short flag group is part of the argument or not unless - // you know what is a defined flag or not. We leave that kind of processing - // to the parser. - return true; + } else { + // I actually think this will never happen. To call this method you have + // to access a specific option_result for an option. If there's a + // specific option_result then the option was found. If the option + // requires an argument then it will definitely have an argument + // otherwise the parser would have complained. + return t; + } } - -inline -bool is_valid_flag_definition( - const char* s) +template +option_result::operator T() const { - auto len = std::strlen(s); + return this->as(); +} - // The shortest possible flag has two characters: a hyphen and an - // alpha-numeric character. - if (len < 2) { - return false; - } +template<> +inline option_result::operator bool() const +{ + return this->arg != nullptr; +} - // All flags must start with a hyphen. - if (s[0] != '-') { - return false; - } +inline std::size_t +option_results::count() const +{ + return this->all.size(); +} - // Shift the name forward by a character to account for the initial hyphen. - // This means if s was originally "-v" then name will be "v". - const char* name = s + 1; +inline option_result & +option_results::operator[](std::size_t index) +{ + return this->all[index]; +} - // Check if we're dealing with a long flag. - bool is_long = false; - if (s[1] == '-') { - is_long = true; +inline const option_result & +option_results::operator[](std::size_t index) const +{ + return this->all[index]; +} - // Just -- is not a valid flag. - if (len == 2) { - return false; +template +T +option_results::as() const +{ + if (this->all.size() == 0) { + throw std::out_of_range("no option arguments to convert"); + } + return this->all.back().as(); +} + +template +T +option_results::as(const T &t) const +{ + if (this->all.size() == 0) { return t; } + return this->all.back().as(t); +} + +template +option_results::operator T() const +{ + return this->as(); +} + +template<> +inline option_results::operator bool() const +{ + return this->all.size() > 0; +} + +inline bool +parser_results::has_option(const std::string &name) const +{ + const auto it = this->options.find(name); + return (it != this->options.end()) && it->second.all.size() > 0; +} + +inline option_results & +parser_results::operator[](const std::string &name) +{ + return this->options.at(name); +} + +inline const option_results & +parser_results::operator[](const std::string &name) const +{ + return this->options.at(name); +} + +inline std::size_t +parser_results::count() const +{ + return this->pos.size(); +} + +inline const char * +parser_results::operator[](std::size_t index) const +{ + return this->pos[index]; +} + +template +T +parser_results::as(std::size_t i) const +{ + return convert::arg(this->pos[i]); +} + +template +std::vector +parser_results::all_as() const +{ + std::vector v(this->pos.size()); + std::transform(this->pos.begin(), this->pos.end(), v.begin(), + [](const char *arg) { return convert::arg(arg); }); + return v; +} + +inline bool +definition::wants_no_arguments() const +{ + return this->num_args == 0; +} + +inline bool +definition::requires_arguments() const +{ + return this->num_args > 0; +} + +inline bool +cmd_line_arg_is_option_flag(const char *s) +{ + auto len = std::strlen(s); + + // The shortest possible flag has two characters: a hyphen and an + // alpha-numeric character. + if (len < 2) { return false; } + + // All flags must start with a hyphen. + if (s[0] != '-') { return false; } + + // Shift the name forward by a character to account for the initial hyphen. + // This means if s was originally "-v" then name will be "v". + const char *name = s + 1; + + // Check if we're dealing with a long flag. + bool is_long = false; + if (s[1] == '-') { + is_long = true; + + // Just -- is not a valid flag. + if (len == 2) { return false; } + + // Shift the name forward to account for the extra hyphen. This means if s + // was originally "--output" then name will be "output". + name = s + 2; } - // Shift the name forward to account for the extra hyphen. This means if s - // was originally "--output" then name will be "output". - name = s + 2; - } + // The first character of the flag name must be alpha-numeric. This is to + // prevent things like "---a" from being valid flags. + len = std::strlen(name); + if (!std::isalnum(name[0])) { return false; } - // The first character of the flag name must be alpha-numeric. This is to - // prevent things like "---a" from being valid flags. - len = std::strlen(name); - if (!std::isalnum(name[0])) { - return false; - } + // At this point in is_valid_flag_definition() we would check if the short + // flag has only one character. At command line specification you can group + // short flags together or even add an argument to a short flag without a + // space delimiter. Thus we don't check if this has only one character + // because it might not. - // If this is a short flag then it must only have one character. - if (!is_long && len > 1) { - return false; - } + // If this is a long flag then we expect all characters *up to* an equal sign + // to be alpha-numeric or a hyphen. After the equal sign you are specify the + // argument to a long flag which can be basically anything. + if (is_long) { + bool encountered_equal = false; + return std::all_of(name, name + len, [&](const char &c) { + if (encountered_equal) { + return true; + } else { + if (c == '=') { + encountered_equal = true; + return true; + } + return std::isalnum(c) || c == '-'; + } + }); + } - // The rest of the characters must be alpha-numeric, but long flags are - // allowed to have hyphens too. - return std::all_of(name + 1, name + len, [&](const char& c) { - return std::isalnum(c) || (c == '-' && is_long); + // At this point we are not dealing with a long flag. We already checked that + // the first character is alpha-numeric so we've got the case of a single + // short flag covered. This might be a short flag group though and we might + // be tempted to check that each character of the short flag group is + // alpha-numeric. However, you can specify the argument for a short flag + // without a space delimiter (e.g. "-I/usr/local/include") so you can't tell + // if the rest of a short flag group is part of the argument or not unless + // you know what is a defined flag or not. We leave that kind of processing + // to the parser. + return true; +} + +inline bool +is_valid_flag_definition(const char *s) +{ + auto len = std::strlen(s); + + // The shortest possible flag has two characters: a hyphen and an + // alpha-numeric character. + if (len < 2) { return false; } + + // All flags must start with a hyphen. + if (s[0] != '-') { return false; } + + // Shift the name forward by a character to account for the initial hyphen. + // This means if s was originally "-v" then name will be "v". + const char *name = s + 1; + + // Check if we're dealing with a long flag. + bool is_long = false; + if (s[1] == '-') { + is_long = true; + + // Just -- is not a valid flag. + if (len == 2) { return false; } + + // Shift the name forward to account for the extra hyphen. This means if s + // was originally "--output" then name will be "output". + name = s + 2; + } + + // The first character of the flag name must be alpha-numeric. This is to + // prevent things like "---a" from being valid flags. + len = std::strlen(name); + if (!std::isalnum(name[0])) { return false; } + + // If this is a short flag then it must only have one character. + if (!is_long && len > 1) { return false; } + + // The rest of the characters must be alpha-numeric, but long flags are + // allowed to have hyphens too. + return std::all_of(name + 1, name + len, [&](const char &c) { + return std::isalnum(c) || (c == '-' && is_long); }); } - -inline -bool flag_is_short( - const char* s) +inline bool +flag_is_short(const char *s) { - return s[0] == '-' && std::isalnum(s[1]); + return s[0] == '-' && std::isalnum(s[1]); } - -inline -bool parser_map::known_short_flag( - const char flag) const +inline bool +parser_map::known_short_flag(const char flag) const { - return this->short_map[flag] != nullptr; + return this->short_map[flag] != nullptr; } - -inline -const definition* parser_map::get_definition_for_short_flag( - const char flag) const +inline const definition * +parser_map::get_definition_for_short_flag(const char flag) const { - return this->short_map[flag]; + return this->short_map[flag]; } - -inline -bool parser_map::known_long_flag( - const std::string& flag) const +inline bool +parser_map::known_long_flag(const std::string &flag) const { - const auto existing_long_flag = this->long_map.find(flag); - return existing_long_flag != long_map.end(); + const auto existing_long_flag = this->long_map.find(flag); + return existing_long_flag != long_map.end(); } - -inline -const definition* parser_map::get_definition_for_long_flag( - const std::string& flag) const +inline const definition * +parser_map::get_definition_for_long_flag(const std::string &flag) const { - const auto existing_long_flag = this->long_map.find(flag); - if (existing_long_flag == long_map.end()) { - return nullptr; - } - return existing_long_flag->second; + const auto existing_long_flag = this->long_map.find(flag); + if (existing_long_flag == long_map.end()) { return nullptr; } + return existing_long_flag->second; } - -inline -parser_map validate_definitions( - const std::vector& definitions) +inline parser_map +validate_definitions(const std::vector &definitions) { - std::unordered_map long_map; - parser_map map {{{nullptr}}, std::move(long_map)}; + std::unordered_map long_map; + parser_map map{{{nullptr}}, std::move(long_map)}; - for (auto& defn : definitions) { + for (auto &defn : definitions) { - if (defn.flags.size() == 0) { - std::ostringstream msg; - msg << "option \"" << defn.name << "\" has no flag definitions"; - throw invalid_flag(msg.str()); - } - - for (auto& flag : defn.flags) { - - if (!is_valid_flag_definition(flag.data())) { - std::ostringstream msg; - msg << "flag \"" << flag << "\" specified for option \"" << defn.name - << "\" is invalid"; - throw invalid_flag(msg.str()); - } - - if (flag_is_short(flag.data())) { - const int short_flag_letter = flag[1]; - const auto existing_short_flag = map.short_map[short_flag_letter]; - bool short_flag_already_exists = (existing_short_flag != nullptr); - if (short_flag_already_exists) { - std::ostringstream msg; - msg << "duplicate short flag \"" << flag - << "\" found, specified by both option \"" << defn.name - << "\" and option \"" << existing_short_flag->name; - throw invalid_flag(msg.str()); + if (defn.flags.size() == 0) { + std::ostringstream msg; + msg << "option \"" << defn.name << "\" has no flag definitions"; + throw invalid_flag(msg.str()); } - map.short_map[short_flag_letter] = &defn; - continue; - } - // If we're here then this is a valid, long-style flag. - if (map.known_long_flag(flag)) { - const auto existing_long_flag = map.get_definition_for_long_flag(flag); - std::ostringstream msg; - msg << "duplicate long flag \"" << flag - << "\" found, specified by both option \"" << defn.name - << "\" and option \"" << existing_long_flag->name; - throw invalid_flag(msg.str()); - } - map.long_map.insert(std::make_pair(flag, &defn)); + for (auto &flag : defn.flags) { + + if (!is_valid_flag_definition(flag.data())) { + std::ostringstream msg; + msg << "flag \"" << flag << "\" specified for option \"" + << defn.name << "\" is invalid"; + throw invalid_flag(msg.str()); + } + + if (flag_is_short(flag.data())) { + const int short_flag_letter = flag[1]; + const auto existing_short_flag = + map.short_map[short_flag_letter]; + bool short_flag_already_exists = + (existing_short_flag != nullptr); + if (short_flag_already_exists) { + std::ostringstream msg; + msg << "duplicate short flag \"" << flag + << "\" found, specified by both option \"" << defn.name + << "\" and option \"" << existing_short_flag->name; + throw invalid_flag(msg.str()); + } + map.short_map[short_flag_letter] = &defn; + continue; + } + + // If we're here then this is a valid, long-style flag. + if (map.known_long_flag(flag)) { + const auto existing_long_flag = + map.get_definition_for_long_flag(flag); + std::ostringstream msg; + msg << "duplicate long flag \"" << flag + << "\" found, specified by both option \"" << defn.name + << "\" and option \"" << existing_long_flag->name; + throw invalid_flag(msg.str()); + } + map.long_map.insert(std::make_pair(flag, &defn)); + } } - } - return map; + return map; } - -inline -parser_results parser::parse(int argc, const char** argv) const +inline parser_results +parser::parse(int argc, const char **argv) const { - // Inspect each definition to see if its valid. You may wonder "why don't - // you do this validation on construction?" I had thought about it but - // realized that since I've made the parser an aggregate type (granted it - // just "aggregates" a single vector) I would need to track any changes to - // the definitions vector and re-run the validity check in order to - // maintain this expected "validity invariant" on the object. That would - // then require hiding the definitions vector as a private entry and then - // turning the parser into a thin interface (by re-exposing setters and - // getters) to the vector methods just so that I can catch when the - // definition has been modified. It seems much simpler to just enforce the - // validity when you actually want to parser because it's at the moment of - // parsing that you know the definitions are complete. - parser_map map = validate_definitions(this->definitions); + // Inspect each definition to see if its valid. You may wonder "why don't + // you do this validation on construction?" I had thought about it but + // realized that since I've made the parser an aggregate type (granted it + // just "aggregates" a single vector) I would need to track any changes to + // the definitions vector and re-run the validity check in order to + // maintain this expected "validity invariant" on the object. That would + // then require hiding the definitions vector as a private entry and then + // turning the parser into a thin interface (by re-exposing setters and + // getters) to the vector methods just so that I can catch when the + // definition has been modified. It seems much simpler to just enforce the + // validity when you actually want to parser because it's at the moment of + // parsing that you know the definitions are complete. + parser_map map = validate_definitions(this->definitions); - // Initialize the parser results that we'll be returning. Store the program - // name (assumed to be the first command line argument) and initialize - // everything else as empty. - std::unordered_map options {}; - std::vector pos; - parser_results results {argv[0], std::move(options), std::move(pos)}; + // Initialize the parser results that we'll be returning. Store the program + // name (assumed to be the first command line argument) and initialize + // everything else as empty. + std::unordered_map options{}; + std::vector pos; + parser_results results{argv[0], std::move(options), std::move(pos)}; - // Add an empty option result for each definition. - for (const auto& defn : this->definitions) { - option_results opt_results {{}}; - results.options.insert( - std::make_pair(defn.name, opt_results)); - } + // Add an empty option result for each definition. + for (const auto &defn : this->definitions) { + option_results opt_results{{}}; + results.options.insert(std::make_pair(defn.name, opt_results)); + } - // Don't start off ignoring flags. We only ignore flags after a -- shows up - // in the command line arguments. - bool ignore_flags = false; + // Don't start off ignoring flags. We only ignore flags after a -- shows up + // in the command line arguments. + bool ignore_flags = false; - // Keep track of any options that are expecting arguments. - const char* last_flag_expecting_args = nullptr; - option_result* last_option_expecting_args = nullptr; - unsigned int num_option_args_to_consume = 0; + // Keep track of any options that are expecting arguments. + const char *last_flag_expecting_args = nullptr; + option_result *last_option_expecting_args = nullptr; + unsigned int num_option_args_to_consume = 0; - // Get pointers to pointers so we can treat the raw pointer array as an - // iterator for standard library algorithms. This isn't used yet but can be - // used to template this function to work on iterators over strings or - // C-strings. - const char** arg_i = argv + 1; - const char** arg_end = argv + argc; + // Get pointers to pointers so we can treat the raw pointer array as an + // iterator for standard library algorithms. This isn't used yet but can be + // used to template this function to work on iterators over strings or + // C-strings. + const char **arg_i = argv + 1; + const char **arg_end = argv + argc; - while (arg_i != arg_end) { - auto arg_i_cstr = *arg_i; - auto arg_i_len = std::strlen(arg_i_cstr); + while (arg_i != arg_end) { + auto arg_i_cstr = *arg_i; + auto arg_i_len = std::strlen(arg_i_cstr); - // Some behavior to note: if the previous option is expecting an argument - // then the next entry will be treated as a positional argument even if - // it looks like a flag. - bool treat_as_positional_argument = ( - ignore_flags - || num_option_args_to_consume > 0 - || !cmd_line_arg_is_option_flag(arg_i_cstr) - ); - if (treat_as_positional_argument) { + // Some behavior to note: if the previous option is expecting an argument + // then the next entry will be treated as a positional argument even if + // it looks like a flag. + bool treat_as_positional_argument = + (ignore_flags || num_option_args_to_consume > 0 + || !cmd_line_arg_is_option_flag(arg_i_cstr)); + if (treat_as_positional_argument) { + + // If last option is expecting some specific positive number of + // arguments then give this argument to that option, *regardless of + // whether or not the argument looks like a flag or is the special "--" + // argument*. + if (num_option_args_to_consume > 0) { + last_option_expecting_args->arg = arg_i_cstr; + --num_option_args_to_consume; + ++arg_i; + continue; + } + + // Now we check if this is just "--" which is a special argument that + // causes all following arguments to be treated as non-options and is + // itselve discarded. + if (std::strncmp(arg_i_cstr, "--", 2) == 0 && arg_i_len == 2) { + ignore_flags = true; + ++arg_i; + continue; + } + + // If there are no expectations for option arguments then simply use + // this argument as a positional argument. + results.pos.push_back(arg_i_cstr); + ++arg_i; + continue; + } + + // Reset the "expecting argument" state. + last_flag_expecting_args = nullptr; + last_option_expecting_args = nullptr; + num_option_args_to_consume = 0; + + // If we're at this point then we're definitely dealing with something + // that is flag-like and has hyphen as the first character and has a + // length of at least two characters. How we handle this potential flag + // depends on whether or not it is a long-option so we check that first. + bool is_long_flag = (arg_i_cstr[1] == '-'); + + if (is_long_flag) { + + // Long flags have a complication: their arguments can be specified + // using an '=' character right inside the argument. That means an + // argument like "--output=foobar.txt" is actually an option with flag + // "--output" and argument "foobar.txt". So we look for the first + // instance of the '=' character and keep it in long_flag_arg. If + // long_flag_arg is nullptr then we didn't find '='. We need the + // flag_len to construct long_flag_str below. + auto long_flag_arg = std::strchr(arg_i_cstr, '='); + std::size_t flag_len = arg_i_len; + if (long_flag_arg != nullptr) { + flag_len = long_flag_arg - arg_i_cstr; + } + std::string long_flag_str(arg_i_cstr, flag_len); + + if (!map.known_long_flag(long_flag_str)) { + std::ostringstream msg; + msg << "found unexpected flag: " << long_flag_str; + throw unexpected_option_error(msg.str()); + } + + const auto defn = map.get_definition_for_long_flag(long_flag_str); + + if (long_flag_arg != nullptr && defn->num_args == 0) { + std::ostringstream msg; + msg << "found argument for option not expecting an argument: " + << arg_i_cstr; + throw unexpected_argument_error(msg.str()); + } + + // We've got a legitimate, known long flag option so we add an option + // result. This option result initially has an arg of nullptr, but that + // might change in the following block. + auto &opt_results = results.options[defn->name]; + option_result opt_result{nullptr}; + opt_results.all.push_back(std::move(opt_result)); + + if (defn->requires_arguments()) { + bool there_is_an_equal_delimited_arg = + (long_flag_arg != nullptr); + if (there_is_an_equal_delimited_arg) { + // long_flag_arg would be "=foo" in the "--output=foo" case so we + // increment by 1 to get rid of the equal sign. + opt_results.all.back().arg = long_flag_arg + 1; + } else { + last_flag_expecting_args = arg_i_cstr; + last_option_expecting_args = &(opt_results.all.back()); + num_option_args_to_consume = defn->num_args; + } + } + + ++arg_i; + continue; + } + + // If we've made it here then we're looking at either a short flag or a + // group of short flags. Short flags can be grouped together so long as + // they don't require any arguments unless the option that does is the + // last in the group ("-o x -v" is okay, "-vo x" is okay, "-ov x" is + // not). So starting after the dash we're going to process each character + // as if it were a separate flag. Note "sf_idx" stands for "short flag + // index". + for (std::size_t sf_idx = 1; sf_idx < arg_i_len; ++sf_idx) { + const auto short_flag = arg_i_cstr[sf_idx]; + + if (!std::isalnum(short_flag)) { + std::ostringstream msg; + msg << "found non-alphanumeric character '" + << arg_i_cstr[sf_idx] << "' in flag group '" << arg_i_cstr + << "'"; + throw std::domain_error(msg.str()); + } + + if (!map.known_short_flag(short_flag)) { + std::ostringstream msg; + msg << "found unexpected flag '" << arg_i_cstr[sf_idx] + << "' in flag group '" << arg_i_cstr << "'"; + throw unexpected_option_error(msg.str()); + } + + auto defn = map.get_definition_for_short_flag(short_flag); + auto &opt_results = results.options[defn->name]; + + // Create an option result with an empty argument (for now) and add it + // to this option's results. + option_result opt_result{nullptr}; + opt_results.all.push_back(std::move(opt_result)); + + if (defn->requires_arguments()) { + + // If this short flag's option requires an argument and we're the + // last flag in the short flag group then just put the parser into + // "expecting argument for last option" state and move onto the next + // command line argument. + bool is_last_short_flag_in_group = (sf_idx == arg_i_len - 1); + if (is_last_short_flag_in_group) { + last_flag_expecting_args = arg_i_cstr; + last_option_expecting_args = &(opt_results.all.back()); + num_option_args_to_consume = defn->num_args; + break; + } + + // If this short flag's option requires an argument and we're NOT the + // last flag in the short flag group then we automatically consume + // the rest of the short flag group as the argument for this flag. + // This is how we get the POSIX behavior of being able to specify a + // flag's arguments without a white space delimiter (e.g. + // "-I/usr/local/include"). + opt_results.all.back().arg = arg_i_cstr + sf_idx + 1; + break; + } + } - // If last option is expecting some specific positive number of - // arguments then give this argument to that option, *regardless of - // whether or not the argument looks like a flag or is the special "--" - // argument*. - if (num_option_args_to_consume > 0) { - last_option_expecting_args->arg = arg_i_cstr; - --num_option_args_to_consume; ++arg_i; continue; - } - - // Now we check if this is just "--" which is a special argument that - // causes all following arguments to be treated as non-options and is - // itselve discarded. - if (std::strncmp(arg_i_cstr, "--", 2) == 0 && arg_i_len == 2) { - ignore_flags = true; - ++arg_i; - continue; - } - - // If there are no expectations for option arguments then simply use - // this argument as a positional argument. - results.pos.push_back(arg_i_cstr); - ++arg_i; - continue; } - // Reset the "expecting argument" state. - last_flag_expecting_args = nullptr; - last_option_expecting_args = nullptr; - num_option_args_to_consume = 0; - - // If we're at this point then we're definitely dealing with something - // that is flag-like and has hyphen as the first character and has a - // length of at least two characters. How we handle this potential flag - // depends on whether or not it is a long-option so we check that first. - bool is_long_flag = (arg_i_cstr[1] == '-'); - - if (is_long_flag) { - - // Long flags have a complication: their arguments can be specified - // using an '=' character right inside the argument. That means an - // argument like "--output=foobar.txt" is actually an option with flag - // "--output" and argument "foobar.txt". So we look for the first - // instance of the '=' character and keep it in long_flag_arg. If - // long_flag_arg is nullptr then we didn't find '='. We need the - // flag_len to construct long_flag_str below. - auto long_flag_arg = std::strchr(arg_i_cstr, '='); - std::size_t flag_len = arg_i_len; - if (long_flag_arg != nullptr) { - flag_len = long_flag_arg - arg_i_cstr; - } - std::string long_flag_str(arg_i_cstr, flag_len); - - if (!map.known_long_flag(long_flag_str)) { + // If we're done with all of the arguments but are still expecting + // arguments for a previous option then we haven't satisfied that option. + // This is an error. + if (num_option_args_to_consume > 0) { std::ostringstream msg; - msg << "found unexpected flag: " << long_flag_str; - throw unexpected_option_error(msg.str()); - } - - const auto defn = map.get_definition_for_long_flag(long_flag_str); - - if (long_flag_arg != nullptr && defn->num_args == 0) { - std::ostringstream msg; - msg << "found argument for option not expecting an argument: " - << arg_i_cstr; - throw unexpected_argument_error(msg.str()); - } - - // We've got a legitimate, known long flag option so we add an option - // result. This option result initially has an arg of nullptr, but that - // might change in the following block. - auto& opt_results = results.options[defn->name]; - option_result opt_result {nullptr}; - opt_results.all.push_back(std::move(opt_result)); - - if (defn->requires_arguments()) { - bool there_is_an_equal_delimited_arg = (long_flag_arg != nullptr); - if (there_is_an_equal_delimited_arg) { - // long_flag_arg would be "=foo" in the "--output=foo" case so we - // increment by 1 to get rid of the equal sign. - opt_results.all.back().arg = long_flag_arg + 1; - } else { - last_flag_expecting_args = arg_i_cstr; - last_option_expecting_args = &(opt_results.all.back()); - num_option_args_to_consume = defn->num_args; - } - } - - ++arg_i; - continue; + msg << "last option \"" << last_flag_expecting_args + << "\" expects an argument but the parser ran out of command line " + << "arguments to parse"; + throw option_lacks_argument_error(msg.str()); } - // If we've made it here then we're looking at either a short flag or a - // group of short flags. Short flags can be grouped together so long as - // they don't require any arguments unless the option that does is the - // last in the group ("-o x -v" is okay, "-vo x" is okay, "-ov x" is - // not). So starting after the dash we're going to process each character - // as if it were a separate flag. Note "sf_idx" stands for "short flag - // index". - for (std::size_t sf_idx = 1; sf_idx < arg_i_len; ++sf_idx) { - const auto short_flag = arg_i_cstr[sf_idx]; - - if (!std::isalnum(short_flag)) { - std::ostringstream msg; - msg << "found non-alphanumeric character '" << arg_i_cstr[sf_idx] - << "' in flag group '" << arg_i_cstr << "'"; - throw std::domain_error(msg.str()); - } - - if (!map.known_short_flag(short_flag)) { - std::ostringstream msg; - msg << "found unexpected flag '" << arg_i_cstr[sf_idx] - << "' in flag group '" << arg_i_cstr << "'"; - throw unexpected_option_error(msg.str()); - } - - auto defn = map.get_definition_for_short_flag(short_flag); - auto& opt_results = results.options[defn->name]; - - // Create an option result with an empty argument (for now) and add it - // to this option's results. - option_result opt_result {nullptr}; - opt_results.all.push_back(std::move(opt_result)); - - if (defn->requires_arguments()) { - - // If this short flag's option requires an argument and we're the - // last flag in the short flag group then just put the parser into - // "expecting argument for last option" state and move onto the next - // command line argument. - bool is_last_short_flag_in_group = (sf_idx == arg_i_len - 1); - if (is_last_short_flag_in_group) { - last_flag_expecting_args = arg_i_cstr; - last_option_expecting_args = &(opt_results.all.back()); - num_option_args_to_consume = defn->num_args; - break; - } - - // If this short flag's option requires an argument and we're NOT the - // last flag in the short flag group then we automatically consume - // the rest of the short flag group as the argument for this flag. - // This is how we get the POSIX behavior of being able to specify a - // flag's arguments without a white space delimiter (e.g. - // "-I/usr/local/include"). - opt_results.all.back().arg = arg_i_cstr + sf_idx + 1; - break; - } - } - - ++arg_i; - continue; - } - - // If we're done with all of the arguments but are still expecting - // arguments for a previous option then we haven't satisfied that option. - // This is an error. - if (num_option_args_to_consume > 0) { - std::ostringstream msg; - msg << "last option \"" << last_flag_expecting_args - << "\" expects an argument but the parser ran out of command line " - << "arguments to parse"; - throw option_lacks_argument_error(msg.str()); - } - - return results; + return results; } - -inline -parser_results parser::parse(int argc, char** argv) const +inline parser_results +parser::parse(int argc, char **argv) const { - return parse(argc, const_cast(argv)); + return parse(argc, const_cast(argv)); } - namespace convert { - - /** +/** * @brief * Templated function for conversion to T using the @ref std::strtol() * function. This is used for anything long length or shorter (long, int, * short, char). */ - template inline - T long_(const char* arg) - { - char* endptr = nullptr; +template +inline T +long_(const char *arg) +{ + char *endptr = nullptr; errno = 0; T ret = static_cast(std::strtol(arg, &endptr, 0)); if (endptr == arg) { - std::ostringstream msg; - msg << "unable to convert argument to integer: \"" << arg << "\""; - throw std::invalid_argument(msg.str()); + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); } if (errno == ERANGE) { - throw std::out_of_range("argument numeric value out of range"); + throw std::out_of_range("argument numeric value out of range"); } return ret; - } +} - - /** +/** * @brief * Templated function for conversion to T using the @ref std::strtoll() * function. This is used for anything long long length or shorter (long * long). */ - template inline - T long_long_(const char* arg) - { - char* endptr = nullptr; +template +inline T +long_long_(const char *arg) +{ + char *endptr = nullptr; errno = 0; T ret = static_cast(std::strtoll(arg, &endptr, 0)); if (endptr == arg) { - std::ostringstream msg; - msg << "unable to convert argument to integer: \"" << arg << "\""; - throw std::invalid_argument(msg.str()); + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); } if (errno == ERANGE) { - throw std::out_of_range("argument numeric value out of range"); + throw std::out_of_range("argument numeric value out of range"); } return ret; - } +} +#define DEFINE_CONVERSION_FROM_LONG_(TYPE) \ + template<> \ + inline TYPE arg(const char *arg) \ + { \ + return long_(arg); \ + } -#define DEFINE_CONVERSION_FROM_LONG_(TYPE) \ - template <> inline \ - TYPE arg(const char* arg) \ - { \ - return long_(arg); \ - } - - DEFINE_CONVERSION_FROM_LONG_(char) - DEFINE_CONVERSION_FROM_LONG_(unsigned char) - DEFINE_CONVERSION_FROM_LONG_(signed char) - DEFINE_CONVERSION_FROM_LONG_(short) - DEFINE_CONVERSION_FROM_LONG_(unsigned short) - DEFINE_CONVERSION_FROM_LONG_(int) - DEFINE_CONVERSION_FROM_LONG_(unsigned int) - DEFINE_CONVERSION_FROM_LONG_(long) - DEFINE_CONVERSION_FROM_LONG_(unsigned long) +DEFINE_CONVERSION_FROM_LONG_(char) +DEFINE_CONVERSION_FROM_LONG_(unsigned char) +DEFINE_CONVERSION_FROM_LONG_(signed char) +DEFINE_CONVERSION_FROM_LONG_(short) +DEFINE_CONVERSION_FROM_LONG_(unsigned short) +DEFINE_CONVERSION_FROM_LONG_(int) +DEFINE_CONVERSION_FROM_LONG_(unsigned int) +DEFINE_CONVERSION_FROM_LONG_(long) +DEFINE_CONVERSION_FROM_LONG_(unsigned long) #undef DEFINE_CONVERSION_FROM_LONG_ +#define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE) \ + template<> \ + inline TYPE arg(const char *arg) \ + { \ + return long_long_(arg); \ + } -#define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE) \ - template <> inline \ - TYPE arg(const char* arg) \ - { \ - return long_long_(arg); \ - } - - DEFINE_CONVERSION_FROM_LONG_LONG_(long long) - DEFINE_CONVERSION_FROM_LONG_LONG_(unsigned long long) +DEFINE_CONVERSION_FROM_LONG_LONG_(long long) +DEFINE_CONVERSION_FROM_LONG_LONG_(unsigned long long) #undef DEFINE_CONVERSION_FROM_LONG_LONG_ - - template <> inline - bool arg(const char* arg) - { +template<> +inline bool +arg(const char *arg) +{ return argagg::convert::arg(arg) != 0; - } +} - - template <> inline - float arg(const char* arg) - { - char* endptr = nullptr; +template<> +inline float +arg(const char *arg) +{ + char *endptr = nullptr; errno = 0; float ret = std::strtof(arg, &endptr); if (endptr == arg) { - std::ostringstream msg; - msg << "unable to convert argument to integer: \"" << arg << "\""; - throw std::invalid_argument(msg.str()); + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); } if (errno == ERANGE) { - throw std::out_of_range("argument numeric value out of range"); + throw std::out_of_range("argument numeric value out of range"); } return ret; - } +} - - template <> inline - double arg(const char* arg) - { - char* endptr = nullptr; +template<> +inline double +arg(const char *arg) +{ + char *endptr = nullptr; errno = 0; double ret = std::strtod(arg, &endptr); if (endptr == arg) { - std::ostringstream msg; - msg << "unable to convert argument to integer: \"" << arg << "\""; - throw std::invalid_argument(msg.str()); + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); } if (errno == ERANGE) { - throw std::out_of_range("argument numeric value out of range"); + throw std::out_of_range("argument numeric value out of range"); } return ret; - } +} - - template <> inline - const char* arg(const char* arg) - { +template<> +inline const char * +arg(const char *arg) +{ return arg; - } +} - - template <> inline - std::string arg(const char* arg) - { +template<> +inline std::string +arg(const char *arg) +{ return std::string(arg); - } - } +}// namespace convert -inline -fmt_ostream::fmt_ostream(std::ostream& output) -: std::ostringstream(), output(output) -{ -} - - -inline -fmt_ostream::~fmt_ostream() -{ - output << fmt_string(this->str()); -} +inline fmt_ostream::fmt_ostream(std::ostream &output) + : std::ostringstream(), + output(output) +{} +inline fmt_ostream::~fmt_ostream() { output << fmt_string(this->str()); } #ifdef __unix__ - -inline -std::string fmt_string(const std::string& s) +inline std::string +fmt_string(const std::string &s) { - constexpr int read_end = 0; - constexpr int write_end = 1; + constexpr int read_end = 0; + constexpr int write_end = 1; - // TODO (vnguyen): This function overall needs to handle possible error - // returns from the various syscalls. + // TODO (vnguyen): This function overall needs to handle possible error + // returns from the various syscalls. - int read_pipe[2]; - int write_pipe[2]; - if (pipe(read_pipe) == -1) { - return s; - } - if (pipe(write_pipe) == -1) { - return s; - } + int read_pipe[2]; + int write_pipe[2]; + if (pipe(read_pipe) == -1) { return s; } + if (pipe(write_pipe) == -1) { return s; } + + auto parent_pid = fork(); + bool is_fmt_proc = (parent_pid == 0); + if (is_fmt_proc) { + dup2(write_pipe[read_end], STDIN_FILENO); + dup2(read_pipe[write_end], STDOUT_FILENO); + close(write_pipe[read_end]); + close(write_pipe[write_end]); + close(read_pipe[read_end]); + close(read_pipe[write_end]); + const char *argv[] = {"fmt", NULL}; + execvp(const_cast(argv[0]), const_cast(argv)); + } - auto parent_pid = fork(); - bool is_fmt_proc = (parent_pid == 0); - if (is_fmt_proc) { - dup2(write_pipe[read_end], STDIN_FILENO); - dup2(read_pipe[write_end], STDOUT_FILENO); close(write_pipe[read_end]); - close(write_pipe[write_end]); - close(read_pipe[read_end]); close(read_pipe[write_end]); - const char* argv[] = {"fmt", NULL}; - execvp(const_cast(argv[0]), const_cast(argv)); - } + auto fmt_write_fd = write_pipe[write_end]; + auto write_result = write(fmt_write_fd, s.c_str(), s.length()); + if (write_result != static_cast(s.length())) { return s; } + close(fmt_write_fd); - close(write_pipe[read_end]); - close(read_pipe[write_end]); - auto fmt_write_fd = write_pipe[write_end]; - auto write_result = write(fmt_write_fd, s.c_str(), s.length()); - if (write_result != static_cast(s.length())) { + auto fmt_read_fd = read_pipe[read_end]; + std::ostringstream os; + char buf[64]; + while (true) { + auto read_count = + read(fmt_read_fd, reinterpret_cast(buf), sizeof(buf)); + if (read_count <= 0) { break; } + os.write(buf, static_cast(read_count)); + } + close(fmt_read_fd); + + return os.str(); +} + +#else// #ifdef __unix__ + +inline std::string +fmt_string(const std::string &s) +{ return s; - } - close(fmt_write_fd); - - auto fmt_read_fd = read_pipe[read_end]; - std::ostringstream os; - char buf[64]; - while (true) { - auto read_count = read( - fmt_read_fd, reinterpret_cast(buf), sizeof(buf)); - if (read_count <= 0) { - break; - } - os.write(buf, static_cast(read_count)); - } - close(fmt_read_fd); - - return os.str(); } +#endif// #ifdef __unix__ -#else // #ifdef __unix__ +}// namespace argagg +// +namespace ulib { +using parser = argagg::parser; +using parser_results = argagg::parser_results; +using option_results = argagg::option_results; +using option_result = argagg::option_result; +using definition = argagg::definition; +using fmt_ostream = argagg::fmt_ostream; -inline -std::string fmt_string(const std::string& s) +inline std::string +fmt_string(const std::string &s) { - return s; -} + return argagg::fmt_string(s); +}; +}// namespace ulib - -#endif // #ifdef __unix__ - - -} // namespace argagg - - -inline -std::ostream& operator << (std::ostream& os, const argagg::parser& x) +inline std::ostream & +operator<<(std::ostream &os, const argagg::parser &x) { - for (auto& definition : x.definitions) { - os << " "; - for (auto& flag : definition.flags) { - os << flag; - if (flag != definition.flags.back()) { - os << ", "; - } + for (auto &definition : x.definitions) { + os << " "; + for (auto &flag : definition.flags) { + os << flag; + if (flag != definition.flags.back()) { os << ", "; } + } + os << std::endl; + os << " " << definition.help << std::endl; } - os << std::endl; - os << " " << definition.help << std::endl; - } - return os; + return os; } - -#endif // ARGAGG_ARGAGG_ARGAGG_HPP +#endif// ARGAGG_ARGAGG_ARGAGG_HPP