From 03f11c6fddf7e7720eccf4a90e79d7844ee361f5 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Fri, 19 Apr 2024 15:07:30 +0200 Subject: [PATCH] some fixes and renames --- README.md | 18 +- include/ap/argument_parser.hpp | 419 ++++++++++++++++----------------- 2 files changed, 218 insertions(+), 219 deletions(-) diff --git a/README.md b/README.md index 7bf39c7..8946903 100644 --- a/README.md +++ b/README.md @@ -160,10 +160,10 @@ Parameters which can be specified for both positional and optional arguments inc .action([](const double& value) { return 1. / value; }); ``` - Actions can also be used to perform some value checking logic, e.g. the predefined `check_file_exists_action` which checks if a file with a given name exists: + Actions can also be used to perform some value checking logic, e.g. the predefined `check_file_exists` which checks if a file with a given name exists: ```c++ parser.add_optional_argument("input", "i") - .action(ap::action::check_file_exists_action); + .action(ap::action::check_file_exists()); ``` **Optional argument specific parameters** @@ -231,7 +231,7 @@ The supported default arguments are: ```c++ // equivalent to: parser.add_positional_argument("input") - .action(ap::action::check_file_exists_action) + .action(ap::action::check_file_exists()) .help("Input file path"); ``` @@ -262,14 +262,14 @@ The supported default arguments are: parser.add_optional_argument("input", "i") .required() .nargs(1) - .action(ap::action::check_file_exists_action) + .action(ap::action::check_file_exists()) .help("Input file path"); // multi_input - equivalent to: parser.add_optional_argument("input", "i") .required() .nargs(ap::nargs::at_least(1)) - .action(ap::action::check_file_exists_action) + .action(ap::action::check_file_exists()) .help("Input files paths"); ``` @@ -298,14 +298,14 @@ To parse the command-line arguments use the `argument_parser::parse_args` method // power.cpp #include -#include #include +#include int main(int argc, char* argv[]) { // create the parser class instance ap::argument_parser parser; parser.program_name("power calculator") - .program_description("calculates the value of an expression: base & exponent"); + .program_description("calculates the value of an expression: base ^ exponent"); // add arguments parser.add_positional_argument("base").help("the exponentation base value"); @@ -363,11 +363,11 @@ int main(int argc, char* argv[]) { **NOTE:** For each positional argument there must be **exactly one value**. ```shell - ./test_program + ./power # out: # [ERROR] : No values parsed for a required argument [base] # power calculator - # calculates the value of an expression: base & exponent + # calculates the value of an expression: base ^ exponent # [base] : the exponentation base value # [exponent,e] : the exponent value # [help,h] : Display help message diff --git a/include/ap/argument_parser.hpp b/include/ap/argument_parser.hpp index 2ed25a3..e82cbbe 100644 --- a/include/ap/argument_parser.hpp +++ b/include/ap/argument_parser.hpp @@ -106,213 +106,6 @@ inline constexpr bool is_valid_type_v = std::disjunction_v_default; - } - - /** - * @brief Checks if a given value count is within the range. - * @param n The value count to check. - * @return Ordering relationship between the count and the range. - */ - [[nodiscard]] std::weak_ordering contains(const range::count_type n) const noexcept { - if (not (this->_nlow.has_value() or this->_nhigh.has_value())) - return std::weak_ordering::equivalent; - - if (this->_nlow.has_value() and this->_nhigh.has_value()) { - if (n < this->_nlow.value()) - return std::weak_ordering::less; - - if (n > this->_nhigh.value()) - return std::weak_ordering::greater; - - return std::weak_ordering::equivalent; - } - - if (this->_nlow.has_value()) - return (n < this->_nlow.value()) ? std::weak_ordering::less : std::weak_ordering::equivalent; - - return (n > this->_nhigh.value()) ? std::weak_ordering::greater : std::weak_ordering::equivalent; - } - - friend range at_least(const count_type) noexcept; - friend range more_than(const count_type) noexcept; - friend range less_than(const count_type) noexcept; - friend range up_to(const count_type) noexcept; - friend range any() noexcept; - -private: - /** - * @brief Private constructor: creates a possibly unbound range - * @param nlow The optional lower bound of the range. - * @param nhigh The optional upper bound of the range. - */ - range(const std::optional nlow, const std::optional nhigh) - : _nlow(nlow), _nhigh(nhigh) {} - - std::optional _nlow; - std::optional _nhigh; - bool _default = true; - - static constexpr count_type _ndefault = 1; -}; - -/** - * @brief `range` class builder function. Creates a range [n, inf]. - * @param n The lower bound. - * @return Built `range` class instance. - */ -[[nodiscard]] inline range at_least(const range::count_type n) noexcept { - return range(n, std::nullopt); -} - -/** - * @brief `range` class builder function. Creates a range [n + 1, inf]. - * @param n The lower bound. - * @return Built `range` class instance. - */ -[[nodiscard]] inline range more_than(const range::count_type n) noexcept { - return range(n + 1, std::nullopt); -} - -/** - * @brief `range` class builder function. Creates a range [0, n - 1]. - * @param n The upper bound - * @return Built `range` class instance. - */ -[[nodiscard]] inline range less_than(const range::count_type n) noexcept { - return range(std::nullopt, n - 1); -} - -/** - * @brief `range` class builder function. Creates a range [0, n]. - * @param n The upper bound - * @return Built `range` class instance. - */ -[[nodiscard]] inline range up_to(const range::count_type n) noexcept { - return range(std::nullopt, n); -} - -/** - * @brief `range` class builder function. Creates a range [0, inf]. - * @return Built `range` class instance. - */ -[[nodiscard]] inline range any() noexcept { - return range(std::nullopt, std::nullopt); -} - -} // namespace nargs - -/// @brief Defines valued argument action traits. -struct valued_action { - template - using type = std::function; -}; - -/// @brief Defines void argument action traits. -struct void_action { - template - using type = std::function; -}; - -// TODO: on_read_action - -/// @brief Argument action handling utility. -namespace action { - -/// @brief Internal argument action handling utility -namespace detail { - -/** - * @brief The concept is satisfied when `AS` is either a valued or void argument action - * @tparam AS The action specifier type. - */ -template -concept valid_action_specifier = ap::utility::is_valid_type_v; - -/// @brief Template argument action callable type alias. -template -using callable_type = typename AS::template type; - -/// @brief Template argument action callabla variant type alias. -template -using action_variant_type = - std::variant, callable_type>; - -/** - * @brief Checks if an argument action variant holds a void action. - * @tparam T The argument value type. - * @param action The action variant. - * @return True if the held action is a void action. - */ -template -[[nodiscard]] inline bool is_void_action(const action_variant_type& action) noexcept { - return std::holds_alternative>(action); -} - -} // namespace detail - -/// @brief Returns a default argument action. -template -detail::callable_type default_action() noexcept { - return [](T&) {}; -} - -/// @brief Returns a predefined action for file name handling arguments. Checks whether a file with the given name exists. -inline detail::callable_type check_file_exists_action() noexcept { - return [](std::string& file_path) { - if (not std::filesystem::exists(file_path)) { - std::cerr << "[ERROR] : File " + file_path + " does not exists!"; - std::exit(EXIT_FAILURE); - } - }; -} - -// TODO: on_flag_action - -} // namespace action - /// @brief Internal argument handling utility. namespace argument::detail { @@ -623,6 +416,212 @@ class invalid_nvalues_error : public argument_parser_error { } // namespace error +/// @brief Argument's number of values management utility. +namespace nargs { + +/// @brief Argument's number of values managing class. +class range { +public: + using count_type = std::size_t; + + /// @brief Default constructor: creates range [1, 1]. + range() : _nlow(_ndefault), _nhigh(_ndefault) {} + + /** + * @brief Exact count constructor: creates range [n, n]. + * @param n Expected value count. + */ + explicit range(const count_type n) : _nlow(n), _nhigh(n), _default(n == _ndefault) {} + + /** + * @brief Concrete range constructor: creates range [nlow, nhigh]. + * @param nlow The lower bound. + * @param nhigh The upper bound. + */ + range(const count_type nlow, const count_type nhigh) + : _nlow(nlow), _nhigh(nhigh), _default(nlow == _ndefault and nhigh == _ndefault) {} + + /// @brief Copy constructor + range(const range&) = default; + + /// @brief Move constructor + range(range&&) = default; + + /// @brief Copy assignmner constructor + range& operator=(const range&) = default; + + /// @brief Move assignmner constructor + range& operator=(range&&) = default; + + /// @brief Class destructor. + ~range() = default; + + /// @return True if the range is [1, 1]. + [[nodiscard]] bool is_default() const noexcept { + return this->_default; + } + + /** + * @brief Checks if a given value count is within the range. + * @param n The value count to check. + * @return Ordering relationship between the count and the range. + */ + [[nodiscard]] std::weak_ordering contains(const range::count_type n) const noexcept { + if (not (this->_nlow.has_value() or this->_nhigh.has_value())) + return std::weak_ordering::equivalent; + + if (this->_nlow.has_value() and this->_nhigh.has_value()) { + if (n < this->_nlow.value()) + return std::weak_ordering::less; + + if (n > this->_nhigh.value()) + return std::weak_ordering::greater; + + return std::weak_ordering::equivalent; + } + + if (this->_nlow.has_value()) + return (n < this->_nlow.value()) ? std::weak_ordering::less : std::weak_ordering::equivalent; + + return (n > this->_nhigh.value()) ? std::weak_ordering::greater : std::weak_ordering::equivalent; + } + + friend range at_least(const count_type) noexcept; + friend range more_than(const count_type) noexcept; + friend range less_than(const count_type) noexcept; + friend range up_to(const count_type) noexcept; + friend range any() noexcept; + +private: + /** + * @brief Private constructor: creates a possibly unbound range + * @param nlow The optional lower bound of the range. + * @param nhigh The optional upper bound of the range. + */ + range(const std::optional nlow, const std::optional nhigh) + : _nlow(nlow), _nhigh(nhigh) {} + + std::optional _nlow; + std::optional _nhigh; + bool _default = true; + + static constexpr count_type _ndefault = 1; +}; + +/** + * @brief `range` class builder function. Creates a range [n, inf]. + * @param n The lower bound. + * @return Built `range` class instance. + */ +[[nodiscard]] inline range at_least(const range::count_type n) noexcept { + return range(n, std::nullopt); +} + +/** + * @brief `range` class builder function. Creates a range [n + 1, inf]. + * @param n The lower bound. + * @return Built `range` class instance. + */ +[[nodiscard]] inline range more_than(const range::count_type n) noexcept { + return range(n + 1, std::nullopt); +} + +/** + * @brief `range` class builder function. Creates a range [0, n - 1]. + * @param n The upper bound + * @return Built `range` class instance. + */ +[[nodiscard]] inline range less_than(const range::count_type n) noexcept { + return range(std::nullopt, n - 1); +} + +/** + * @brief `range` class builder function. Creates a range [0, n]. + * @param n The upper bound + * @return Built `range` class instance. + */ +[[nodiscard]] inline range up_to(const range::count_type n) noexcept { + return range(std::nullopt, n); +} + +/** + * @brief `range` class builder function. Creates a range [0, inf]. + * @return Built `range` class instance. + */ +[[nodiscard]] inline range any() noexcept { + return range(std::nullopt, std::nullopt); +} + +} // namespace nargs + +/// @brief Defines valued argument action traits. +struct valued_action { + template + using type = std::function; +}; + +/// @brief Defines void argument action traits. +struct void_action { + template + using type = std::function; +}; + +// TODO: +// * on_read_action +// * on_flag_action + +/// @brief Argument action handling utility. +namespace action { + +/// @brief Internal argument action handling utility +namespace detail { + +/** + * @brief The concept is satisfied when `AS` is either a valued or void argument action + * @tparam AS The action specifier type. + */ +template +concept valid_action_specifier = ap::utility::is_valid_type_v; + +/// @brief Template argument action callable type alias. +template +using callable_type = typename AS::template type; + +/// @brief Template argument action callabla variant type alias. +template +using action_variant_type = + std::variant, callable_type>; + +/** + * @brief Checks if an argument action variant holds a void action. + * @tparam T The argument value type. + * @param action The action variant. + * @return True if the held action is a void action. + */ +template +[[nodiscard]] inline bool is_void_action(const action_variant_type& action) noexcept { + return std::holds_alternative>(action); +} + +} // namespace detail + +/// @brief Returns a default argument action. +template +detail::callable_type default_action() noexcept { + return [](T&) {}; +} + +/// @brief Returns a predefined action for file name handling arguments. Checks whether a file with the given name exists. +inline detail::callable_type check_file_exists() noexcept { + return [](std::string& file_path) { + if (not std::filesystem::exists(file_path)) + throw argument_parser_error("[ERROR] : File " + file_path + " does not exists!"); + }; +} + +} // namespace action + + /// @brief Namespace containing classes and utilities for handling command-line arguments. namespace argument { @@ -1431,7 +1430,7 @@ class argument_parser { switch (arg_discriminator) { case default_posarg::input: this->add_positional_argument("input") - .action(ap::action::check_file_exists_action()) + .action(ap::action::check_file_exists()) .help("Input file path"); break; @@ -1455,7 +1454,7 @@ class argument_parser { this->add_optional_argument("input", "i") .required() .nargs(1) - .action(ap::action::check_file_exists_action()) + .action(ap::action::check_file_exists()) .help("Input file path"); break; @@ -1467,7 +1466,7 @@ class argument_parser { this->add_optional_argument("input", "i") .required() .nargs(ap::nargs::at_least(1)) - .action(ap::action::check_file_exists_action()) + .action(ap::action::check_file_exists()) .help("Input files paths"); break;