From a030e97f369ef3c400fe760ef4e95f841bc1cb25 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 19 Sep 2026 20:53:45 +0200 Subject: [PATCH 1/5] util/concepts.hpp -> traits.hpp --- docs/groups.dox | 5 ++ docs/tutorial.md | 10 +-- include/argon/action/predefined.hpp | 10 +-- include/argon/action/types.hpp | 8 +-- include/argon/action/util/concepts.hpp | 6 +- include/argon/action/util/helpers.hpp | 8 ++- include/argon/argument.hpp | 70 +++++++++---------- include/argon/argument_parser.hpp | 34 ++++----- include/argon/detail/argument_base.hpp | 12 +--- include/argon/detail/help_builder.hpp | 10 +-- .../argon/{util/concepts.hpp => traits.hpp} | 44 +++++++----- include/argon/util/string.hpp | 8 +-- .../include/argument_parser_test_fixture.hpp | 6 +- tests/include/argument_test_fixture.hpp | 6 +- tests/include/utility.hpp | 4 +- 15 files changed, 125 insertions(+), 116 deletions(-) rename include/argon/{util/concepts.hpp => traits.hpp} (85%) diff --git a/docs/groups.dox b/docs/groups.dox index 129940d1..6846b2c6 100644 --- a/docs/groups.dox +++ b/docs/groups.dox @@ -7,3 +7,8 @@ * @defgroup util Utility * @brief Helper functions, types, concepts, etc. */ + + /** + * @defgroup traits Traits & Concepts + * @brief Type trait and concept definitions. + */ diff --git a/docs/tutorial.md b/docs/tutorial.md index 046ae6b8..13745142 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -716,7 +716,7 @@ Command Result Validates that the value is strictly greater than `lower_bound`. ```cpp - template + template argon::action::util::callable_type gt(const T lower_bound) noexcept; ``` @@ -725,7 +725,7 @@ Command Result Validates that the value is greater than or equal to `lower_bound`. ```cpp - template + template argon::action::util::callable_type geq(const T lower_bound) noexcept; ``` @@ -734,7 +734,7 @@ Command Result Validates that the value is strictly less than `upper_bound`. ```cpp - template + template argon::action::util::callable_type lt(const T upper_bound) noexcept; ``` @@ -743,7 +743,7 @@ Command Result Validates that the value is less than or equal to `upper_bound`. ```cpp - template + template argon::action::util::callable_type leq(const T upper_bound) noexcept; ``` @@ -752,7 +752,7 @@ Command Result Checks if the value is within the given interval. Bound inclusivity is customizable using template parameters. ```cpp - template + template argon::action::util::callable_type within( const T lower_bound, const T upper_bound ) noexcept; diff --git a/include/argon/action/predefined.hpp b/include/argon/action/predefined.hpp index 95cc0878..11d3e575 100644 --- a/include/argon/action/predefined.hpp +++ b/include/argon/action/predefined.hpp @@ -54,7 +54,7 @@ inline util::callable_type check_file_ * @tparam T The *arithmetic* value type. * @param lower_bound The exclusive lower bound to validate against. */ -template +template util::callable_type gt(const T lower_bound) noexcept { return [lower_bound](const T& value) { if (not (value > lower_bound)) @@ -69,7 +69,7 @@ util::callable_type gt(const T lower_bound) noex * @tparam T The *arithmetic* value type. * @param lower_bound The inclusive lower bound to validate against. */ -template +template util::callable_type geq(const T lower_bound) noexcept { return [lower_bound](const T& value) { if (! (value >= lower_bound)) @@ -84,7 +84,7 @@ util::callable_type geq(const T lower_bound) noe * @tparam T The *arithmetic* value type. * @param lower_bound The exclusive upper bound to validate against. */ -template +template util::callable_type lt(const T upper_bound) noexcept { return [upper_bound](const T& value) { if (! (value < upper_bound)) @@ -99,7 +99,7 @@ util::callable_type lt(const T upper_bound) noex * @tparam T The *arithmetic* value type. * @param lower_bound The inclusive upper bound to validate against. */ -template +template util::callable_type leq(const T upper_bound) noexcept { return [upper_bound](const T& value) { if (! (value <= upper_bound)) @@ -120,7 +120,7 @@ util::callable_type leq(const T upper_bound) noe * @param lower_bound The lower bound of the interval. * @param upper_bound The upper bound of the interval. */ -template +template util::callable_type within( const T lower_bound, const T upper_bound ) noexcept { diff --git a/include/argon/action/types.hpp b/include/argon/action/types.hpp index f243f6ec..71495fc2 100644 --- a/include/argon/action/types.hpp +++ b/include/argon/action/types.hpp @@ -9,7 +9,7 @@ #pragma once -#include "argon/util/concepts.hpp" +#include "argon/traits.hpp" #include @@ -22,7 +22,7 @@ namespace argon::action_type { * performs some logic on it without modifying it. */ struct observe { - template + template using type = std::function; }; @@ -33,7 +33,7 @@ struct observe { * returns a new value with which the argument will be initialized. */ struct transform { - template + template using type = std::function; }; @@ -44,7 +44,7 @@ struct transform { * already initialized argument. */ struct modify { - template + template using type = std::function; }; diff --git a/include/argon/action/util/concepts.hpp b/include/argon/action/util/concepts.hpp index 963289bb..18fde402 100644 --- a/include/argon/action/util/concepts.hpp +++ b/include/argon/action/util/concepts.hpp @@ -7,6 +7,8 @@ * @brief Defines action-related concepts. */ +// TODO: move to action/traits.hpp, change namesapce to argon::action::traits + #pragma once #include "argon/action/types.hpp" @@ -23,7 +25,7 @@ namespace argon::action::util { */ template concept c_value_action_specifier = - argon::util::c_one_of; + argon::traits::c_one_of; /** * @brief The concept is satisfied when `AS` is a valid *on-flag* action action specifier. @@ -31,7 +33,7 @@ concept c_value_action_specifier = * @ingroup util */ template -concept c_flag_action_specifier = argon::util::c_one_of; +concept c_flag_action_specifier = argon::traits::c_one_of; /** * @brief The concept is satisfied when `AS` is a valid action action specifier. diff --git a/include/argon/action/util/helpers.hpp b/include/argon/action/util/helpers.hpp index 48dea4dc..41390961 100644 --- a/include/argon/action/util/helpers.hpp +++ b/include/argon/action/util/helpers.hpp @@ -7,6 +7,8 @@ * @brief Defines general action-related helper utility. */ +// TODO: move to action/util.hpp + #pragma once #include "argon/action/util/concepts.hpp" @@ -18,11 +20,11 @@ namespace argon::action::util { /// @brief Template argument action callable type alias. /// @ingroup util -template +template using callable_type = typename AS::template type; /// @brief Template argument action callabla variant type alias. -template +template using value_action_variant_type = std::variant< callable_type, callable_type, @@ -33,7 +35,7 @@ using value_action_variant_type = std::variant< * @tparam T The argument's value type * @ingroup util */ -template +template struct apply_visitor { using value_type = T; diff --git a/include/argon/argument.hpp b/include/argon/argument.hpp index 97a23f6c..425e0706 100644 --- a/include/argon/argument.hpp +++ b/include/argon/argument.hpp @@ -11,8 +11,8 @@ #include "argon/detail/argument_base.hpp" #include "argon/detail/help_builder.hpp" #include "argon/nargs/range.hpp" +#include "argon/traits.hpp" #include "argon/types.hpp" -#include "argon/util/concepts.hpp" #ifdef AP_TESTING @@ -53,7 +53,7 @@ enum class argument_type : bool { positional, optional }; * @tparam ArgT The argument type, either @ref argon::argument_type::positional or @ref argon::argument_type::optional. * @tparam T The value type accepted by the argument (defaults to std::string). */ -template +template class argument : public detail::typed_argument_base { public: using value_type = T; ///< The argument's value type alias. @@ -208,7 +208,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ argument& greedy(const bool value = true) noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { this->_greedy = value; return *this; @@ -221,7 +221,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ argument& nargs(const nargs::range& range) noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { this->_nargs_range = range; return *this; @@ -234,7 +234,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ argument& nargs(const count_type n) noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { return this->nargs(nargs::range(n)); } @@ -247,7 +247,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ argument& nargs(const count_type lower, const count_type upper) noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { return this->nargs(nargs::range(lower, upper)); } @@ -264,7 +264,7 @@ class argument : public detail::typed_argument_base { */ template argument& action(F&& action) noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { using callable_type = action::util::callable_type; this->_value_actions.emplace_back(std::forward(action)); @@ -296,9 +296,9 @@ class argument : public detail::typed_argument_base { * @note - `value_type` must not be `none_type` and must be equality comparable * @note - `CR` must be a range such that its value type is convertible to the argument's `value_type` */ - template CR> + template CR> argument& choices(const CR& choices) noexcept - requires(not util::c_is_none and std::equality_comparable) + requires(not traits::c_is_none and std::equality_comparable) { for (const auto& choice : choices) this->_choices.emplace_back(choice); @@ -312,7 +312,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type` and is equality comparable. */ argument& choices(std::initializer_list choices) noexcept - requires(not util::c_is_none and std::equality_comparable) + requires(not traits::c_is_none and std::equality_comparable) { return this->choices<>(choices); } @@ -325,7 +325,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type` and is equality comparable. */ argument& choices(const std::convertible_to auto&... choices) noexcept - requires(not util::c_is_none and std::equality_comparable) + requires(not traits::c_is_none and std::equality_comparable) { (this->_choices.emplace_back(choices), ...); return *this; @@ -338,9 +338,9 @@ class argument : public detail::typed_argument_base { * @attention Setting the default values resets the `required` attribute to `false`. * @note The method is enabled only if `value_type` is not `none_type`. */ - template CR> + template CR> argument& default_values(const CR& values) noexcept - requires(not util::c_is_none and std::equality_comparable) + requires(not traits::c_is_none and std::equality_comparable) { for (const auto& value : values) this->_default_values.emplace_back(value); @@ -356,7 +356,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ argument& default_values(std::initializer_list values) noexcept - requires(not util::c_is_none and std::equality_comparable) + requires(not traits::c_is_none and std::equality_comparable) { return this->default_values<>(values); } @@ -369,7 +369,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ argument& default_values(const std::convertible_to auto&... values) noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { (this->_default_values.emplace_back(values), ...); this->_required = false; @@ -383,9 +383,9 @@ class argument : public detail::typed_argument_base { * @return Reference to the optional argument instance. * @note The method is enabled only for optional arguments and if `value_type` is not `none_type`. */ - template CR> + template CR> argument& implicit_values(const CR& values) noexcept - requires(not util::c_is_none and type == argument_type::optional) + requires(not traits::c_is_none and type == argument_type::optional) { for (const auto& value : values) this->_implicit_values.emplace_back(value); @@ -399,7 +399,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only for optional arguments and if `value_type` is not `none_type`. */ argument& implicit_values(std::initializer_list values) noexcept - requires(not util::c_is_none and type == argument_type::optional) + requires(not traits::c_is_none and type == argument_type::optional) { return this->implicit_values<>(values); } @@ -411,7 +411,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only for optional arguments and if `value_type` is not `none_type`. */ argument& implicit_values(const std::convertible_to auto&... values) noexcept - requires(not util::c_is_none and type == argument_type::optional) + requires(not traits::c_is_none and type == argument_type::optional) { (this->_implicit_values.emplace_back(values), ...); return *this; @@ -432,7 +432,7 @@ class argument : public detail::typed_argument_base { /// @tparam _T The actual type used if the argument's `value_type` is not `none_type`. template using value_arg_specific_type = std::conditional_t< - util::c_is_none, + traits::c_is_none, none_type, _T>; ///< Type alias for value-argument-specific types. @@ -469,7 +469,7 @@ class argument : public detail::typed_argument_base { bld.add_param("suppress group checks", "true"); if (this->_nargs_range != _default_nargs_range) bld.add_param("nargs", this->_nargs_range); - if constexpr (util::c_writable) { + if constexpr (traits::c_writable) { if (not this->_choices.empty()) bld.add_range_param("choices", this->_choices); if (not this->_default_values.empty()) @@ -551,11 +551,11 @@ class argument : public detail::typed_argument_base { * @note If multiple values are available, the first one is returned. * @throws std::logic_error if no values are available. */ - [[nodiscard]] detail::arg_return_type value() const override { + [[nodiscard]] traits::argument_return_type value() const override { if (this->has_parsed_values()) return this->_values.front(); - if constexpr (util::c_is_none) + if constexpr (traits::c_is_none) throw std::logic_error( std::format("No values parsed for argument '{}'.", this->_name.str()) ); @@ -571,7 +571,7 @@ class argument : public detail::typed_argument_base { /// @return Reference to the vector of parsed values for the argument. /// @note For none-type arguments, the method always returns an empty vector. [[nodiscard]] const std::vector& _values_impl() const noexcept - requires(util::c_is_none) + requires(traits::c_is_none) { return this->_values; } @@ -582,7 +582,7 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ [[nodiscard]] const std::vector& _values_impl() const noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { if (this->has_parsed_values()) return this->_values; @@ -597,7 +597,7 @@ class argument : public detail::typed_argument_base { /// @return `true` if the argument has a predefined value, `false` otherwise. [[nodiscard]] bool _has_predefined_values_impl() const noexcept - requires(util::c_is_none) + requires(traits::c_is_none) { return false; } @@ -609,7 +609,7 @@ class argument : public detail::typed_argument_base { * @note - For optional arguments, a predefined value exists if either a default value is set or if the argument has been used and an implicit value is set. */ [[nodiscard]] bool _has_predefined_values_impl() const noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { if constexpr (type == argument_type::positional) return not this->_default_values.empty(); @@ -626,7 +626,7 @@ class argument : public detail::typed_argument_base { * @note - For optional arguments, if the argument has been used, the implicit value list is returned, otherwise the default value list is returned. */ [[nodiscard]] const std::vector& _predefined_values() const - requires(not util::c_is_none) + requires(not traits::c_is_none) { if constexpr (type == argument_type::optional) { if (this->is_used()) { @@ -654,7 +654,7 @@ class argument : public detail::typed_argument_base { /// @return `true` if the given value is a valid choice for the argument, `false` otherwise. [[nodiscard]] bool _is_valid_choice(const value_type& value) const noexcept - requires(not util::c_is_none) + requires(not traits::c_is_none) { return this->_choices.empty() or std::ranges::find(this->_choices, value) != std::ranges::end(this->_choices); @@ -667,7 +667,7 @@ class argument : public detail::typed_argument_base { * @attention Always throws! (`set_value` should never be called for a none-type argument). */ bool _set_value_impl(const std::string& str_value) - requires(util::c_is_none) + requires(traits::c_is_none) { throw parsing_failure(std::format( "Cannot set values for a none-type argument '{}' (value: '{}')", @@ -687,13 +687,13 @@ class argument : public detail::typed_argument_base { * @note The method is enabled only if `value_type` is not `none_type`. */ bool _set_value_impl(const std::string& str_value) - requires(not util::c_is_none) + requires(not traits::c_is_none) { if (not this->_accepts_further_values()) throw parsing_failure::invalid_nvalues(this->_name, std::weak_ordering::greater); value_type value; - if constexpr (util::c_trivially_readable) { + if constexpr (traits::c_trivially_readable) { value = value_type(str_value); } else { @@ -749,7 +749,7 @@ class argument : public detail::typed_argument_base { static constexpr nargs::range _default_nargs_range = (type == argument_type::positional) ? nargs::range(1ull) : nargs::any(); static constexpr nargs::range _default_nargs_range_actual = - util::c_is_none ? nargs::range(0ull) : _default_nargs_range; + traits::c_is_none ? nargs::range(0ull) : _default_nargs_range; }; /** @@ -757,7 +757,7 @@ class argument : public detail::typed_argument_base { * @tparam T The value type accepted by the argument (defaults to std::string). * @see argon::argument */ -template +template using positional_argument = argument; /** @@ -765,7 +765,7 @@ using positional_argument = argument; * @tparam T The value type accepted by the argument (defaults to std::string). * @see argon::argument */ -template +template using optional_argument = argument; } // namespace argon diff --git a/include/argon/argument_parser.hpp b/include/argon/argument_parser.hpp index 19e227d0..cb2e65f9 100644 --- a/include/argon/argument_parser.hpp +++ b/include/argon/argument_parser.hpp @@ -250,7 +250,7 @@ class argument_parser { * @note `arg_discriminators` must be a `std::ranges::range` with the `argon::default_argument` value type. * @return Reference to the argument parser. */ - template ArgvRange> + template ArgvRange> argument_parser& default_arguments(const ArgvRange& arg_discriminators) noexcept { for (const auto arg_discriminator : arg_discriminators) detail::add_default_argument(arg_discriminator, *this); @@ -287,7 +287,7 @@ class argument_parser { * @return Reference to the added positional argument. * @throws argon::invalid_configuration */ - template + template positional_argument& add_positional_argument(const std::string_view base_name) { return this->add_positional_argument(this->_gr_positional_args, base_name); } @@ -299,7 +299,7 @@ class argument_parser { * @return Reference to the added positional argument. * @throws argon::invalid_configuration */ - template + template positional_argument& add_positional_argument( argument_group& group, const std::string_view base_name ) { @@ -326,7 +326,7 @@ class argument_parser { * @return Reference to the added optional argument. * @throws argon::invalid_configuration */ - template + template optional_argument& add_optional_argument( const std::string_view base_name, const detail::argument_name_discriminator name_discr = n_primary @@ -342,7 +342,7 @@ class argument_parser { * @return Reference to the added optional argument. * @throws argon::invalid_configuration */ - template + template optional_argument& add_optional_argument( const std::string_view base_primary_name, const std::string_view base_secondary_name ) { @@ -360,7 +360,7 @@ class argument_parser { * @return Reference to the added optional argument. * @throws std::logic_error, argon::invalid_configuration */ - template + template optional_argument& add_optional_argument( argument_group& group, const std::string_view base_name, @@ -399,7 +399,7 @@ class argument_parser { * @return Reference to the added optional argument. * @throws argon::invalid_configuration */ - template + template optional_argument& add_optional_argument( argument_group& group, const std::string_view base_primary_name, @@ -561,7 +561,7 @@ class argument_parser { * @throws argon::invalid_configuration, argon::parsing_failure * @attention This overload of the `parse_args` function assumes that the program name argument has already been discarded. */ - template ArgvRange> + template ArgvRange> void parse_args(const ArgvRange& argv_rng) { parsing_state state(*this); this->_parse_args_impl(std::ranges::begin(argv_rng), std::ranges::end(argv_rng), state); @@ -600,7 +600,7 @@ class argument_parser { * @note `argv_rng` must be a `std::ranges::forward_range` with a value type convertible to `std::string`. * @attention This overload of the `try_parse_args` function assumes that the program name argument has already been discarded. */ - template ArgvRange> + template ArgvRange> void try_parse_args(const ArgvRange& argv_rng) { try { this->parse_args(argv_rng); @@ -648,7 +648,7 @@ class argument_parser { * @throws argon::invalid_configuration, argon::parsing_failure * @attention This overload of the `parse_known_args` function assumes that the program name argument already been discarded. */ - template ArgvRange> + template ArgvRange> std::vector parse_known_args(const ArgvRange& argv_rng) { parsing_state state(*this, true); this->_parse_args_impl(std::ranges::begin(argv_rng), std::ranges::end(argv_rng), state); @@ -685,7 +685,7 @@ class argument_parser { * @return A vector of unknown argument values. * @attention This overload of the `try_parse_known_args` function assumes that the program name argument has already been discarded. */ - template ArgvRange> + template ArgvRange> std::vector try_parse_known_args(const ArgvRange& argv_rng) { try { return this->parse_known_args(argv_rng); @@ -780,8 +780,8 @@ class argument_parser { * @return The value of the argument. * @throws argon::lookup_failure, argon::type_error */ - template - [[nodiscard]] detail::arg_return_type value(std::string_view arg_name) const { + template + [[nodiscard]] traits::argument_return_type value(std::string_view arg_name) const { const auto arg = this->_get_argument(arg_name); const auto* typed_arg = dynamic_cast*>(arg.get()); @@ -800,7 +800,7 @@ class argument_parser { * @return The value of the argument. * @throws argon::lookup_failure, argon::type_error */ - template U> + template U> [[nodiscard]] T value_or(std::string_view arg_name, U&& fallback_value) const { const auto arg = this->_get_argument(arg_name); @@ -825,7 +825,7 @@ class argument_parser { * @return The values of the argument as a vector. * @throws argon::lookup_failure, argon::type_error */ - template + template [[nodiscard]] const std::vector& values(std::string_view arg_name) const { const auto arg = this->_get_argument(arg_name); @@ -1039,7 +1039,7 @@ class argument_parser { * @param state The current parsing state. * @throws argon::invalid_configuration, argon::parsing_failure */ - template AIt> + template AIt> void _parse_args_impl(AIt args_begin, const AIt args_end, parsing_state& state) { this->_invoked = true; @@ -1099,7 +1099,7 @@ class argument_parser { * @param state The current parsing state. * @return A list of preprocessed command-line argument tokens. */ - template AIt> + template AIt> [[nodiscard]] arg_token_vec_t _tokenize( AIt args_begin, const AIt args_end, const parsing_state& state ) { diff --git a/include/argon/detail/argument_base.hpp b/include/argon/detail/argument_base.hpp index acbfa51f..f89a83c6 100644 --- a/include/argon/detail/argument_base.hpp +++ b/include/argon/detail/argument_base.hpp @@ -11,6 +11,7 @@ #include "argon/detail/argument_name.hpp" #include "argon/detail/help_builder.hpp" +#include "argon/traits.hpp" #include #include @@ -89,19 +90,12 @@ class argument_base { virtual std::weak_ordering nvalues_ordering() const noexcept = 0; }; -/// @brief Returns by value if it is a *small* and trivially copyable. Otherwise, returns by const reference. -/// @note This trait was introduced to avoid problems with `std::vector` reference proxy objects. -/// @todo Move to the `argon::traits` namespace -template -using arg_return_type = - std::conditional_t and sizeof(T) <= sizeof(void*), T, const T&>; - /// @brief Strongly-typed intermediate argument interface -template +template class typed_argument_base : public argument_base { public: /// @return Reference to the stored value of the argument. - virtual arg_return_type value() const = 0; + virtual traits::argument_return_type value() const = 0; /// @return Reference to the vector of parsed values of the argument. virtual const std::vector& values() const = 0; diff --git a/include/argon/detail/help_builder.hpp b/include/argon/detail/help_builder.hpp index fe5d9aef..4a7c2c02 100644 --- a/include/argon/detail/help_builder.hpp +++ b/include/argon/detail/help_builder.hpp @@ -10,7 +10,7 @@ #pragma once #include "argon/detail/argument_name.hpp" -#include "argon/util/concepts.hpp" +#include "argon/traits.hpp" #include "argon/util/string.hpp" #include @@ -49,11 +49,11 @@ class help_builder { /** * @brief Adds a parameter descriptor with the given value. - * @tparam T The type of the parameter; must satisfy the @ref argon::util::c_writable concept. + * @tparam T The type of the parameter; must satisfy the @ref argon::traits::c_writable concept. * @param param_name The parameter's name. * @param value The parameter's value. */ - template + template void add_param(const std::string& param_name, const T& value) { std::ostringstream oss; oss << std::boolalpha << value; @@ -62,13 +62,13 @@ class help_builder { /** * @brief Adds a range parameter descriptor with the given value. - * @tparam R The type of the parameter range. The value type of R must satisfy the @ref argon::util::c_writable concept. + * @tparam R The type of the parameter range. The value type of R must satisfy the @ref argon::traits::c_writable concept. * @param param_name The parameter's name. * @param range The parameter value range. * @param delimiter The delimiter used to separate the range values. */ template - requires(util::c_writable>) + requires(traits::c_writable>) void add_range_param( const std::string& param_name, const R& range, diff --git a/include/argon/util/concepts.hpp b/include/argon/traits.hpp similarity index 85% rename from include/argon/util/concepts.hpp rename to include/argon/traits.hpp index 244fd1da..2a2029b3 100644 --- a/include/argon/util/concepts.hpp +++ b/include/argon/traits.hpp @@ -3,8 +3,8 @@ // Licensed under the MIT License. See the LICENSE file in the project root for full license information. /** - * @file argon/util/concepts.hpp - * @brief Provides the general concept definitions. + * @file argon/traits.hpp + * @brief Provides the general trait and concept definitions. */ #pragma once @@ -14,12 +14,12 @@ #include #include -namespace argon::util { +namespace argon::traits { /** * @brief The concept is satisfied when `T` is @ref argon::none_type. * @tparam T Type to check. - * @ingroup util + * @ingroup traits */ template concept c_is_none = std::same_as; @@ -27,7 +27,7 @@ concept c_is_none = std::same_as; /** * @brief The concept is satisfied when `T` overloads the std::istream operator `>>`. * @tparam T Type to check. - * @ingroup util + * @ingroup traits */ template concept c_readable = requires(T value, std::istream& input_stream) { input_stream >> value; }; @@ -35,7 +35,7 @@ concept c_readable = requires(T value, std::istream& input_stream) { input_strea /** * @brief The concept is satisfied when `T` can be constructed from `const std::string&`. * @tparam T Type to check. - * @ingroup util + * @ingroup traits */ template concept c_trivially_readable = std::constructible_from; @@ -43,7 +43,7 @@ concept c_trivially_readable = std::constructible_from; /** * @brief The concept is satisfied when `T` overloads the std::ostream operator `<<`. * @tparam T Type to check. - * @ingroup util + * @ingroup traits */ template concept c_writable = requires(T value, std::ostream& output_stream) { output_stream << value; }; @@ -51,7 +51,7 @@ concept c_writable = requires(T value, std::ostream& output_stream) { output_str /** * @brief The concept is satisfied when `T` is an arithmetic type. * @tparam T Type to check. - * @ingroup util + * @ingroup traits */ template concept c_arithmetic = std::is_arithmetic_v; @@ -59,25 +59,31 @@ concept c_arithmetic = std::is_arithmetic_v; /** * @brief The concept is used to verify the validity of the arguments' value types. * @tparam T Type to check. - * @ingroup util + * @ingroup traits */ template concept c_argument_value_type = std::same_as or (std::semiregular and (c_trivially_readable or c_readable)); +/// @brief Returns by value if it is a *small* and trivially copyable. Otherwise, returns by const reference. +/// @note This trait was introduced to avoid problems with `std::vector` reference proxy objects. +template +using argument_return_type = + std::conditional_t and sizeof(T) <= sizeof(void*), T, const T&>; + /** * @brief Validates that `T` is the same as one of the types defined by `Types`. * @tparam T Type to check. * @tparam Types The valid types to compare against. - * @ingroup util + * @ingroup traits */ template concept c_one_of = std::disjunction_v...>; /** * @brief Specifies the type validation rule. - * @ingroup util + * @ingroup traits */ enum class type_validator : bool { same, ///< Exact type match. @@ -93,7 +99,7 @@ enum class type_validator : bool { * @tparam T The type to check. * @tparam U The type to check agains. * @tparam TV The validation rule. - * @ingroup util + * @ingroup traits */ template inline constexpr bool is_valid_type_v = false; @@ -102,7 +108,7 @@ inline constexpr bool is_valid_type_v = false; * @brief Checks if `T` and `U` are the same type. * @tparam T The type to check. * @tparam U The type to check agains. - * @ingroup util + * @ingroup traits */ template inline constexpr bool is_valid_type_v = std::same_as; @@ -111,7 +117,7 @@ inline constexpr bool is_valid_type_v = std::same_as * @brief Checks if `T` is convertible to `U`. * @tparam T The type to check. * @tparam U The type to check agains. - * @ingroup util + * @ingroup traits */ template inline constexpr bool is_valid_type_v = @@ -122,7 +128,7 @@ inline constexpr bool is_valid_type_v = * @tparam T The type to check. * @tparam U The type to check agains. * @tparam TV The validation rule (`same` or `convertible`). - * @ingroup util + * @ingroup traits */ template concept c_valid_type = is_valid_type_v; @@ -132,7 +138,7 @@ concept c_valid_type = is_valid_type_v; * @tparam R The range type to check. * @tparam V The expected range value type. * @tparam TV The validation rule (`same` or `convertible`). - * @ingroup util + * @ingroup traits */ template concept c_range_of = @@ -144,7 +150,7 @@ concept c_range_of = * @tparam It The iterator type to check. * @tparam V The expected iterator value type. * @tparam TV The validation rule (`same` or `convertible`). - * @ingroup util + * @ingroup traits */ template concept c_forward_range_of = @@ -156,10 +162,10 @@ concept c_forward_range_of = * @tparam It The iterator type to check. * @tparam V The expected iterator value type. * @tparam TV The validation rule (`same` or `convertible`). - * @ingroup util + * @ingroup traits */ template concept c_forward_iterator_of = std::input_iterator and c_valid_type, V, TV>; -} // namespace argon::util +} // namespace argon::traits diff --git a/include/argon/util/string.hpp b/include/argon/util/string.hpp index 7a9ce424..eb249591 100644 --- a/include/argon/util/string.hpp +++ b/include/argon/util/string.hpp @@ -9,7 +9,7 @@ #pragma once -#include "argon/util/concepts.hpp" +#include "argon/traits.hpp" #include #include @@ -19,11 +19,11 @@ namespace argon::util { /** * @brief Converts a value to `std::string`. - * @tparam T The value type (must satisfy the @ref argon::util::c_writable concept). + * @tparam T The value type (must satisfy the @ref argon::traits::c_writable concept). * @param value The value to convert. * @ingroup util */ -template +template [[nodiscard]] std::string as_string(const T& value) noexcept { std::ostringstream oss; oss << value; @@ -44,7 +44,7 @@ template * @ingroup util */ template -requires(c_writable>) +requires(traits::c_writable>) [[nodiscard]] std::string join(const R& range, const std::string_view delimiter = ", ") { std::ostringstream oss; diff --git a/tests/include/argument_parser_test_fixture.hpp b/tests/include/argument_parser_test_fixture.hpp index 5cc96a66..c4a67dbc 100644 --- a/tests/include/argument_parser_test_fixture.hpp +++ b/tests/include/argument_parser_test_fixture.hpp @@ -9,9 +9,9 @@ using argon::optional_argument; using argon::positional_argument; using argon::detail::argument_name; using argon::detail::argument_token; -using argon::util::c_argument_value_type; -using argon::util::c_forward_iterator_of; -using argon::util::type_validator; +using argon::traits::c_argument_value_type; +using argon::traits::c_forward_iterator_of; +using argon::traits::type_validator; namespace argon_testing { diff --git a/tests/include/argument_test_fixture.hpp b/tests/include/argument_test_fixture.hpp index 1c372fff..34f14288 100644 --- a/tests/include/argument_test_fixture.hpp +++ b/tests/include/argument_test_fixture.hpp @@ -5,11 +5,11 @@ using argon::argument; using argon::argument_type; -using argon::detail::arg_return_type; using argon::detail::argument_name; using argon::detail::help_builder; +using argon::traits::argument_return_type; +using argon::traits::c_argument_value_type; using argon::util::as_string; -using argon::util::c_argument_value_type; namespace argon_testing { @@ -73,7 +73,7 @@ struct argument_test_fixture { } template - [[nodiscard]] arg_return_type get_value(const argument& arg) const { + [[nodiscard]] argument_return_type get_value(const argument& arg) const { return arg.value(); } diff --git a/tests/include/utility.hpp b/tests/include/utility.hpp index 98b2cae3..6c5077a4 100644 --- a/tests/include/utility.hpp +++ b/tests/include/utility.hpp @@ -9,12 +9,12 @@ void discard(T&&) { // do nothing } -template +template bool is_positional(const argon::detail::argument_base& arg) { return dynamic_cast*>(&arg); } -template +template bool is_optional(const argon::detail::argument_base& arg) { return dynamic_cast*>(&arg); } From a9e102aed3bce4c0393b5da9e8c5191c33c782dc Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 19 Sep 2026 20:57:32 +0200 Subject: [PATCH 2/5] action/util/concepts.hpp -> action/traits.hpp --- .../action/{util/concepts.hpp => traits.hpp} | 16 +++++++--------- include/argon/action/util/helpers.hpp | 4 ++-- include/argon/argument.hpp | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) rename include/argon/action/{util/concepts.hpp => traits.hpp} (80%) diff --git a/include/argon/action/util/concepts.hpp b/include/argon/action/traits.hpp similarity index 80% rename from include/argon/action/util/concepts.hpp rename to include/argon/action/traits.hpp index 18fde402..bf58197b 100644 --- a/include/argon/action/util/concepts.hpp +++ b/include/argon/action/traits.hpp @@ -3,12 +3,10 @@ // Licensed under the MIT License. See the LICENSE file in the project root for full license information. /** - * @file argon/action/util/concepts.hpp - * @brief Defines action-related concepts. + * @file argon/action/traits.hpp + * @brief Defines action-related traits and concepts. */ -// TODO: move to action/traits.hpp, change namesapce to argon::action::traits - #pragma once #include "argon/action/types.hpp" @@ -16,12 +14,12 @@ #include #include -namespace argon::action::util { +namespace argon::traits { /** * @brief The concept is satisfied when `AS` is a valid *value* action action specifier. * @tparam AS The action specifier type. - * @ingroup util + * @ingroup traits */ template concept c_value_action_specifier = @@ -30,7 +28,7 @@ concept c_value_action_specifier = /** * @brief The concept is satisfied when `AS` is a valid *on-flag* action action specifier. * @tparam AS The action specifier type. - * @ingroup util + * @ingroup traits */ template concept c_flag_action_specifier = argon::traits::c_one_of; @@ -38,9 +36,9 @@ concept c_flag_action_specifier = argon::traits::c_one_of concept c_action_specifier = c_value_action_specifier or std::same_as; -} // namespace argon::action::util +} // namespace argon::traits diff --git a/include/argon/action/util/helpers.hpp b/include/argon/action/util/helpers.hpp index 41390961..2a7d82b0 100644 --- a/include/argon/action/util/helpers.hpp +++ b/include/argon/action/util/helpers.hpp @@ -11,7 +11,7 @@ #pragma once -#include "argon/action/util/concepts.hpp" +#include "argon/action/traits.hpp" #include #include @@ -20,7 +20,7 @@ namespace argon::action::util { /// @brief Template argument action callable type alias. /// @ingroup util -template +template using callable_type = typename AS::template type; /// @brief Template argument action callabla variant type alias. diff --git a/include/argon/argument.hpp b/include/argon/argument.hpp index 425e0706..68f6dc81 100644 --- a/include/argon/argument.hpp +++ b/include/argon/argument.hpp @@ -262,7 +262,7 @@ class argument : public detail::typed_argument_base { * @note - `value_type` is not `none_type`. * @note - `AS` is a valid value action specifier: `action_type::observe`, `action_type::transform`, `action_type::modify`. */ - template + template argument& action(F&& action) noexcept requires(not traits::c_is_none) { @@ -279,7 +279,7 @@ class argument : public detail::typed_argument_base { * @return Reference to the argument instance. * @note The method is enabled only for optional arguments and if `AS` is `action_type::on_flag`. */ - template + template argument& action(F&& action) noexcept requires(type == argument_type::optional) { From 1462faafde8054980f826869a1cd8403e05b7282 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 19 Sep 2026 20:58:33 +0200 Subject: [PATCH 3/5] aciton/util/helpers.hpp -> action/util.hpp --- include/argon/action/predefined.hpp | 2 +- include/argon/action/{util/helpers.hpp => util.hpp} | 4 +--- include/argon/argument.hpp | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) rename include/argon/action/{util/helpers.hpp => util.hpp} (96%) diff --git a/include/argon/action/predefined.hpp b/include/argon/action/predefined.hpp index 11d3e575..d284adb4 100644 --- a/include/argon/action/predefined.hpp +++ b/include/argon/action/predefined.hpp @@ -6,7 +6,7 @@ #pragma once -#include "argon/action/util/helpers.hpp" +#include "argon/action/util.hpp" #include "argon/exceptions.hpp" #include diff --git a/include/argon/action/util/helpers.hpp b/include/argon/action/util.hpp similarity index 96% rename from include/argon/action/util/helpers.hpp rename to include/argon/action/util.hpp index 2a7d82b0..62bcaa34 100644 --- a/include/argon/action/util/helpers.hpp +++ b/include/argon/action/util.hpp @@ -3,12 +3,10 @@ // Licensed under the MIT License. See the LICENSE file in the project root for full license information. /** - * @file argon/action/util/helpers.hpp + * @file argon/action/util.hpp * @brief Defines general action-related helper utility. */ -// TODO: move to action/util.hpp - #pragma once #include "argon/action/traits.hpp" diff --git a/include/argon/argument.hpp b/include/argon/argument.hpp index 68f6dc81..befffa6d 100644 --- a/include/argon/argument.hpp +++ b/include/argon/argument.hpp @@ -7,7 +7,7 @@ #pragma once #include "argon/action/predefined.hpp" -#include "argon/action/util/helpers.hpp" +#include "argon/action/util.hpp" #include "argon/detail/argument_base.hpp" #include "argon/detail/help_builder.hpp" #include "argon/nargs/range.hpp" From 65531ddee1e722e2a64839616216d0451ed404ef Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 19 Sep 2026 21:07:03 +0200 Subject: [PATCH 4/5] argument_return_type -> argument_result_type --- include/argon/argument.hpp | 2 +- include/argon/argument_parser.hpp | 2 +- include/argon/detail/argument_base.hpp | 2 +- include/argon/traits.hpp | 11 ++++++++--- tests/include/argument_test_fixture.hpp | 4 ++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/argon/argument.hpp b/include/argon/argument.hpp index befffa6d..fd0e67a6 100644 --- a/include/argon/argument.hpp +++ b/include/argon/argument.hpp @@ -551,7 +551,7 @@ class argument : public detail::typed_argument_base { * @note If multiple values are available, the first one is returned. * @throws std::logic_error if no values are available. */ - [[nodiscard]] traits::argument_return_type value() const override { + [[nodiscard]] traits::argument_result_type value() const override { if (this->has_parsed_values()) return this->_values.front(); diff --git a/include/argon/argument_parser.hpp b/include/argon/argument_parser.hpp index cb2e65f9..00d43371 100644 --- a/include/argon/argument_parser.hpp +++ b/include/argon/argument_parser.hpp @@ -781,7 +781,7 @@ class argument_parser { * @throws argon::lookup_failure, argon::type_error */ template - [[nodiscard]] traits::argument_return_type value(std::string_view arg_name) const { + [[nodiscard]] traits::argument_result_type value(std::string_view arg_name) const { const auto arg = this->_get_argument(arg_name); const auto* typed_arg = dynamic_cast*>(arg.get()); diff --git a/include/argon/detail/argument_base.hpp b/include/argon/detail/argument_base.hpp index f89a83c6..d5714771 100644 --- a/include/argon/detail/argument_base.hpp +++ b/include/argon/detail/argument_base.hpp @@ -95,7 +95,7 @@ template class typed_argument_base : public argument_base { public: /// @return Reference to the stored value of the argument. - virtual traits::argument_return_type value() const = 0; + virtual traits::argument_result_type value() const = 0; /// @return Reference to the vector of parsed values of the argument. virtual const std::vector& values() const = 0; diff --git a/include/argon/traits.hpp b/include/argon/traits.hpp index 2a2029b3..12cd22d9 100644 --- a/include/argon/traits.hpp +++ b/include/argon/traits.hpp @@ -66,10 +66,15 @@ concept c_argument_value_type = std::same_as or (std::semiregular and (c_trivially_readable or c_readable)); -/// @brief Returns by value if it is a *small* and trivially copyable. Otherwise, returns by const reference. -/// @note This trait was introduced to avoid problems with `std::vector` reference proxy objects. +/// @brief Defines the return type for argument value getters. +/// +/// Resolves to `T` (by value) if the type is *small* and trivially copyable. +/// Otherwise, resolves to `const T&` (by const reference). +/// +/// @note This trait was introduced to safely bypass the `std::vector` proxy reference trap, +/// ensuring boolean values are always returned by value while complex types avoid deep copies. template -using argument_return_type = +using argument_result_type = std::conditional_t and sizeof(T) <= sizeof(void*), T, const T&>; /** diff --git a/tests/include/argument_test_fixture.hpp b/tests/include/argument_test_fixture.hpp index 34f14288..e2f5b2b4 100644 --- a/tests/include/argument_test_fixture.hpp +++ b/tests/include/argument_test_fixture.hpp @@ -7,7 +7,7 @@ using argon::argument; using argon::argument_type; using argon::detail::argument_name; using argon::detail::help_builder; -using argon::traits::argument_return_type; +using argon::traits::argument_result_type; using argon::traits::c_argument_value_type; using argon::util::as_string; @@ -73,7 +73,7 @@ struct argument_test_fixture { } template - [[nodiscard]] argument_return_type get_value(const argument& arg) const { + [[nodiscard]] argument_result_type get_value(const argument& arg) const { return arg.value(); } From cdfb018ac5df598dd803cabdd24770a6f86fbec9 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 19 Sep 2026 21:13:54 +0200 Subject: [PATCH 5/5] resolved comments --- include/argon/action/util.hpp | 6 +++--- include/argon/traits.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/argon/action/util.hpp b/include/argon/action/util.hpp index 62bcaa34..41bb7192 100644 --- a/include/argon/action/util.hpp +++ b/include/argon/action/util.hpp @@ -18,11 +18,11 @@ namespace argon::action::util { /// @brief Template argument action callable type alias. /// @ingroup util -template +template using callable_type = typename AS::template type; /// @brief Template argument action callabla variant type alias. -template +template using value_action_variant_type = std::variant< callable_type, callable_type, @@ -33,7 +33,7 @@ using value_action_variant_type = std::variant< * @tparam T The argument's value type * @ingroup util */ -template +template struct apply_visitor { using value_type = T; diff --git a/include/argon/traits.hpp b/include/argon/traits.hpp index 12cd22d9..9d14e11f 100644 --- a/include/argon/traits.hpp +++ b/include/argon/traits.hpp @@ -73,7 +73,7 @@ concept c_argument_value_type = /// /// @note This trait was introduced to safely bypass the `std::vector` proxy reference trap, /// ensuring boolean values are always returned by value while complex types avoid deep copies. -template +template using argument_result_type = std::conditional_t and sizeof(T) <= sizeof(void*), T, const T&>;