diff --git a/README.md b/README.md
index 7bf39c7..78fe193 100644
--- a/README.md
+++ b/README.md
@@ -10,18 +10,18 @@ Command-line argument parser for C++20
## Overview
-The `CPP-AP` library has been developed for the *Team Programming* course at the *Wrocław University of Science and Technology*.
-
-Faculty: *W04N - Faculty of Information and Communication Technology*
-
-Field of study: *Algorithmic Computer Science*
-
-
-
The goal of the project was to create a light, intuitive and simple to use command-line argument parser library for the `C++20` and newer standards.
The `CPP-AP` library does not require installing any additional tools or heavy libraries, like with `boost::program_options`. Much like with the `Doctest` framework - the only thing you need to do is copy the `argument_parser.hpp` file into the include directory of your project and you're set to go.
+> **NOTE:** [v1.0](https://github.com/SpectraL519/cpp-ap/commit/9a9e5360766b732f322ae2efe3cf5ec5f9268eef) of the library has been developed for the *Team Programming* course at the *Wrocław University of Science and Technology*.
+>
+> Faculty: *W04N - Faculty of Information and Communication Technology*
+>
+> Field of study: *Algorithmic Computer Science*
+>
+> The project has received the 1st place at the 2024 CreatiWITy competition organized by the faculty. The article in Polish can be found on the [faculty website](https://wit.pwr.edu.pl/aktualnosci/oto-laureaci-konkursu-creatiwity-273.html). Please note that this is not a technical article :)
+
@@ -79,18 +79,18 @@ The parser supports both positional and optional arguments. Both argument types
To add an argument to the parameter's configurations use the following syntax:
```c++
-parser.add__argument("argument_name");
+parser.add__argument("argument");
```
or
```c++
-parser.add__argument("argument_name", "a");
+parser.add__argument("argument", "a");
```
**NOTE:** The library supports any argument value types which meet the following requirements:
-* The `std::ostream& operator<<` must be overloaded for a value type
-* The type must have a copy constructor and an assignment operator
+* The `std::ostream& operator<<` is overloaded for the value type
+* The value type has a copy constructor and an assignment operator
**NOTE:** If the `value_type` is not provided, `std::string` will be used.
@@ -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/change_log.md b/change_log.md
index 74727e9..c56f156 100644
--- a/change_log.md
+++ b/change_log.md
@@ -30,9 +30,10 @@
* Aligned the `.clang-format` configuration file
* Added the `install_clang17_toolchain.sh` env script
* Added the `format` workflow
-* Switched from the `` to the `` library for all current container operations
+* Switched to the `std::ranges` and `std::views` algorithms for all current container operations
* Modified the `argument_name` structure - renamed members: `name` to `primary`, `short_name` to `secondary`
* Added `argument_name::match(string_view)` and `argument_name::match(argument_name)` functions
* Added aliases for default argument enum classes:
* `ap::default_argument::positional` = `ap::default_posarg`
* `ap::default_argument::optional` = `ap::default_optarg`
+* Renamed the predefined: `ap::action::check_file_exists_action` -> `ap::action::check_file_exists`
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;